From d95eabfeec00a2386a64db9461e119ff0c8ac4f6 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 28 Mar 2018 17:39:00 +0200 Subject: [PATCH 01/49] docs: adds provisioning examples for all datasources closes #11363 --- .../features/datasources/cloudwatch.md | 33 +++++++++++++++++++ .../features/datasources/elasticsearch.md | 19 +++++++++++ docs/sources/features/datasources/graphite.md | 17 ++++++++++ docs/sources/features/datasources/influxdb.md | 18 ++++++++++ docs/sources/features/datasources/mysql.md | 17 ++++++++++ docs/sources/features/datasources/opentsdb.md | 18 ++++++++++ docs/sources/features/datasources/postgres.md | 20 +++++++++++ .../features/datasources/prometheus.md | 15 +++++++++ 8 files changed, 157 insertions(+) diff --git a/docs/sources/features/datasources/cloudwatch.md b/docs/sources/features/datasources/cloudwatch.md index f7f8138b5e9..3013f6b1fc9 100644 --- a/docs/sources/features/datasources/cloudwatch.md +++ b/docs/sources/features/datasources/cloudwatch.md @@ -173,3 +173,36 @@ Amazon provides 1 million CloudWatch API requests each month at no additional ch it costs $0.01 per 1,000 GetMetricStatistics or ListMetrics requests. For each query Grafana will issue a GetMetricStatistics request and every time you pick a dimension in the query editor Grafana will issue a ListMetrics request. + +## Configure datasource with provisioning + +Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +Here is an example of how you can configure the Cloudwatch datasource using configuration. + +Using a credentials file +```yaml +apiVersion: 1 + +datasources: + - name: Cloudwatch + type: cloudwatch + jsonData: + authType: credentials + defaultRegion: eu-west-2 +``` + +Using `accessKey` and `secretKey` + +```yaml +apiVersion: 1 + +datasources: + - name: Cloudwatch + type: cloudwatch + jsonData: + authType: keys + defaultRegion: eu-west-2 + secureJsonData: + accessKey: "" + secretKey: "" +``` diff --git a/docs/sources/features/datasources/elasticsearch.md b/docs/sources/features/datasources/elasticsearch.md index 6ce17113a9b..2cfe74588d1 100644 --- a/docs/sources/features/datasources/elasticsearch.md +++ b/docs/sources/features/datasources/elasticsearch.md @@ -137,3 +137,22 @@ Query | You can leave the search query blank or specify a lucene query Time | The name of the time field, needs to be date field. Text | Event description field. Tags | Optional field name to use for event tags (can be an array or a CSV string). + +## Configure datasource with provisioning + +Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +Here are some examples of how you can configure the Cloudwatch datasource using configuration. + +```yaml +apiVersion: 1 + +datasources: + - name: Elastic + type: elasticsearch + access: proxy + database: "[metrics-]YYYY.MM.DD" + url: http://localhost:9200 + jsonData: + interval: Daily + timeField: "@timestamp" +``` \ No newline at end of file diff --git a/docs/sources/features/datasources/graphite.md b/docs/sources/features/datasources/graphite.md index 7c4187da9ae..040a21e5d0c 100644 --- a/docs/sources/features/datasources/graphite.md +++ b/docs/sources/features/datasources/graphite.md @@ -120,3 +120,20 @@ queries via the Dashboard menu / Annotations view. Graphite supports two ways to query annotations. A regular metric query, for this you use the `Graphite query` textbox. A Graphite events query, use the `Graphite event tags` textbox, specify a tag or wildcard (leave empty should also work) + +## Configure datasource with provisioning + +Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +Here are some examples of how you can configure the Cloudwatch datasource using configuration. + +```yaml +apiVersion: 1 + +datasources: + - name: Graphite + type: graphite + access: proxy + url: http://localhost:8080 + jsonData: + graphiteVersion: "1.1" +``` diff --git a/docs/sources/features/datasources/influxdb.md b/docs/sources/features/datasources/influxdb.md index 6d0918a0d01..e36adb3784e 100644 --- a/docs/sources/features/datasources/influxdb.md +++ b/docs/sources/features/datasources/influxdb.md @@ -174,3 +174,21 @@ SELECT title, description from events WHERE $timeFilter order asc For InfluxDB you need to enter a query like in the above example. You need to have the ```where $timeFilter``` part. If you only select one column you will not need to enter anything in the column mapping fields. The Tags field can be a comma separated string. + +## Configure datasource with provisioning + +Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +Here are some examples of how you can configure the Cloudwatch datasource using configuration. + +```yaml +apiVersion: 1 + +datasources: + - name: InfluxDB + type: influxdb + access: proxy + database: site + user: grafana + password: grafana + url: http://localhost:8086 +``` \ No newline at end of file diff --git a/docs/sources/features/datasources/mysql.md b/docs/sources/features/datasources/mysql.md index 7fae7441b6d..dc05b0b31de 100644 --- a/docs/sources/features/datasources/mysql.md +++ b/docs/sources/features/datasources/mysql.md @@ -225,3 +225,20 @@ tags | Optional field name to use for event tags as a comma separated string. ## Alerting Time series queries should work in alerting conditions. Table formatted queries is not yet supported in alert rule conditions. + +## Configure datasource with provisioning + +Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +Here are some examples of how you can configure the Cloudwatch datasource using configuration. + +```yaml +apiVersion: 1 + +datasources: + - name: MySQL + type: mysql + url: localhost:3306 + database: grafana + user: grafana + password: password +``` \ No newline at end of file diff --git a/docs/sources/features/datasources/opentsdb.md b/docs/sources/features/datasources/opentsdb.md index 03795473ff7..f67861e2a51 100644 --- a/docs/sources/features/datasources/opentsdb.md +++ b/docs/sources/features/datasources/opentsdb.md @@ -88,3 +88,21 @@ Query | Description *tag_values(cpu, hostanme, env=$env, region=$region)* | Return tag values for cpu metric, selected env tag value, selected region tag value and tag key hostname For details on OpenTSDB metric queries checkout the official [OpenTSDB documentation](http://opentsdb.net/docs/build/html/index.html) + +## Configure datasource with provisioning + +Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +Here are some examples of how you can configure the Cloudwatch datasource using configuration. + +```yaml +apiVersion: 1 + +datasources: + - name: OpenTsdb + type: opentsdb + access: proxy + url: http://localhost:4242 + jsonData: + tsdbResolution: 1 + tsdbVersion: 1 +``` \ No newline at end of file diff --git a/docs/sources/features/datasources/postgres.md b/docs/sources/features/datasources/postgres.md index 7d52df2fd3e..b70ab1cde04 100644 --- a/docs/sources/features/datasources/postgres.md +++ b/docs/sources/features/datasources/postgres.md @@ -217,3 +217,23 @@ tags | Optional field name to use for event tags as a comma separated string. Time series queries should work in alerting conditions. Table formatted queries is not yet supported in alert rule conditions. + +## Configure datasource with provisioning + +Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +Here are some examples of how you can configure the Cloudwatch datasource using configuration. + +```yaml +apiVersion: 1 + +datasources: + - name: Postgres + type: postgres + url: localhost:5432 + database: grafana + user: grafana + password: password + jsonData: + sslmode: "disable" # disable/require/verify-ca/verify-full + +``` \ No newline at end of file diff --git a/docs/sources/features/datasources/prometheus.md b/docs/sources/features/datasources/prometheus.md index c9bb16441ca..ebd9ce32a47 100644 --- a/docs/sources/features/datasources/prometheus.md +++ b/docs/sources/features/datasources/prometheus.md @@ -100,3 +100,18 @@ The step option is useful to limit the number of events returned from your query ## Getting Grafana metrics into Prometheus Since 4.6.0 Grafana exposes metrics for Prometheus on the `/metrics` endpoint. We also bundle a dashboard within Grafana so you can get started viewing your metrics faster. You can import the bundled dashboard by going to the data source edit page and click the dashboard tab. There you can find a dashboard for Grafana and one for Prometheus. Import and start viewing all the metrics! + +## Configure datasource with provisioning + +Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources +Here are some examples of how you can configure the Cloudwatch datasource using configuration. + +```yaml +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + url: http://localhost:9090 +``` \ No newline at end of file From 77d2ee9add2c45c0bd6ffbffff9ea2648c39e82e Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 27 Mar 2018 23:27:23 +0300 Subject: [PATCH 02/49] Initially move to baron scrollbar --- package.json | 1 + .../core/components/ScrollBar/ScrollBar.tsx | 31 +++-- public/app/core/components/scroll/scroll.ts | 37 ++++- public/app/core/components/search/search.html | 2 + public/app/features/panel/panel_directive.ts | 31 ++++- public/app/partials/dashboard.html | 22 +-- public/app/plugins/panel/graph/legend.ts | 48 +++++-- public/app/plugins/panel/graph/template.ts | 4 +- public/sass/components/_panel_add_panel.scss | 5 +- public/sass/components/_panel_graph.scss | 7 +- public/sass/components/_scrollbar.scss | 126 +++++++++++++++++- public/sass/components/_search.scss | 7 + public/views/index.template.html | 3 +- 13 files changed, 276 insertions(+), 48 deletions(-) diff --git a/package.json b/package.json index 6dcfc16b82b..724bbffb6fa 100644 --- a/package.json +++ b/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", diff --git a/public/app/core/components/ScrollBar/ScrollBar.tsx b/public/app/core/components/ScrollBar/ScrollBar.tsx index 7d9e015df94..a358dc1926a 100644 --- a/public/app/core/components/ScrollBar/ScrollBar.tsx +++ b/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 { 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 { 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 { render() { return ( -
- {this.props.children} +
+
+ {this.props.children} +
+ +
+
+
); } diff --git a/public/app/core/components/scroll/scroll.ts b/public/app/core/components/scroll/scroll.ts index fbf5fd6cd37..ad8b4ed3d8e 100644 --- a/public/app/core/components/scroll/scroll.ts +++ b/public/app/core/components/scroll/scroll.ts @@ -1,15 +1,41 @@ -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 = ` +
+
+
+`; + +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 scrollbar = baron({ + root: scrollRoot[0], + scroller: scroller[0], + bar: '.baron__bar', + barOnCls: '_scrollbar', + scrollingCls: '_scrolling', + track: '.baron__track', }); + let lastPos = 0; appEvents.on( @@ -37,7 +63,8 @@ export function geminiScrollbar() { }); scope.$on('$destroy', () => { - scrollbar.destroy(); + // scrollbar.destroy(); + scrollbar.dispose(); }); }, }; diff --git a/public/app/core/components/search/search.html b/public/app/core/components/search/search.html index acaf0730a6b..afb9e723cad 100644 --- a/public/app/core/components/search/search.html +++ b/public/app/core/components/search/search.html @@ -19,6 +19,7 @@
+
No dashboards matching your query were found.
+
diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index dec7868a553..4edf293c801 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/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,8 +104,26 @@ 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 = ` +
+
+
+ `; + + let scrollRoot = panelContent; + let scroller = panelContent.find(':first-child').find(':first-child'); + scrollRoot.addClass(scrollRootClass); + $(scrollBarHTML).appendTo(scrollRoot); + scroller.addClass(scrollerClass); + + panelScrollbar = baron({ + root: scrollRoot[0], + scroller: scroller[0], + bar: '.baron__bar', + barOnCls: '_scrollbar', + scrollingCls: '_scrolling', }); } }); @@ -110,6 +132,7 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) { ctrl.calculatePanelHeight(); panelHeightUpdated(); $timeout(() => { + resizeScrollableContent(); ctrl.render(); }); }); @@ -199,7 +222,7 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) { } if (panelScrollbar) { - panelScrollbar.update(); + panelScrollbar.dispose(); } }); }, diff --git a/public/app/partials/dashboard.html b/public/app/partials/dashboard.html index 210275d2200..a3d5a4439ee 100644 --- a/public/app/partials/dashboard.html +++ b/public/app/partials/dashboard.html @@ -1,18 +1,20 @@
-
- - +
+
+ + -
- - +
+ + - - + + +
diff --git a/public/app/plugins/panel/graph/legend.ts b/public/app/plugins/panel/graph/legend.ts index d1186ae0b1e..761671c356d 100644 --- a/public/app/plugins/panel/graph/legend.ts +++ b/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'); @@ -250,23 +250,53 @@ 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 = ` +
+
+
+ `; + + let scrollRoot = elem.parent(); + // let scroller = elem.find(':first-child').first(); + let scroller = elem; + + // clear existing scroll bar track to prevent duplication + elem + .parent() + .find('.baron__track') + .remove(); + + scrollRoot.addClass(scrollRootClass); + $(scrollBarHTML).appendTo(scrollRoot); + scroller.addClass(scrollerClass); + + // Fix .graph-legend-content max-height + // Couldn't find how to do it via CSS + const legendHeight = scrollRoot.height(); + elem.css('max-height', legendHeight); + + 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); } } function destroyScrollbar() { if (legendScrollbar) { - legendScrollbar.destroy(); + legendScrollbar.dispose(); legendScrollbar = undefined; } } diff --git a/public/app/plugins/panel/graph/template.ts b/public/app/plugins/panel/graph/template.ts index 0b9eb8227df..c897327fe1a 100644 --- a/public/app/plugins/panel/graph/template.ts +++ b/public/app/plugins/panel/graph/template.ts @@ -3,7 +3,9 @@ var template = `
-
+
+
+
`; diff --git a/public/sass/components/_panel_add_panel.scss b/public/sass/components/_panel_add_panel.scss index 51754a54d92..ee5d757d841 100644 --- a/public/sass/components/_panel_add_panel.scss +++ b/public/sass/components/_panel_add_panel.scss @@ -1,5 +1,9 @@ .add-panel { height: 100%; + + .baron__root { + height: calc(100% - 43px); + } } .add-panel__header { @@ -39,7 +43,6 @@ flex-direction: row; flex-wrap: wrap; overflow: auto; - height: calc(100% - 43px); align-content: flex-start; justify-content: space-around; position: relative; diff --git a/public/sass/components/_panel_graph.scss b/public/sass/components/_panel_graph.scss index e15cd576367..5885b769564 100644 --- a/public/sass/components/_panel_graph.scss +++ b/public/sass/components/_panel_graph.scss @@ -53,7 +53,7 @@ max-height: 30%; margin: 0; text-align: center; - padding-top: 6px; + // padding-top: 6px; position: relative; .popover-content { @@ -61,6 +61,11 @@ } } +.graph-legend-content { + position: relative; + padding-top: 6px; +} + .graph-legend-icon { position: relative; padding-right: 4px; diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index 42818e786f6..216ff1fa1c4 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -9,6 +9,11 @@ -ms-touch-action: auto; } +// ._scrollbar { +// overflow-x: hidden !important; +// overflow-y: auto; +// } + /* * Scrollbar rail styles */ @@ -104,13 +109,19 @@ // Srollbars // -::-webkit-scrollbar { - width: 8px; - height: 8px; -} +// ::-webkit-scrollbar { +// width: 8px; +// height: 8px; +// } -::-webkit-scrollbar:hover { - height: 8px; +// ::-webkit-scrollbar:hover { +// height: 8px; +// } + +::-webkit-scrollbar { + // Hide system scrollbar (Mac OS X) + width: 0; + height: 0; } ::-webkit-scrollbar-button:start:decrement, @@ -172,3 +183,106 @@ border-top: 1px solid $scrollbarBorder; border-left: 1px solid $scrollbarBorder; } + +// Baron styles + +.baron { + display: inline-block; + overflow: hidden; +} + +.baron__clipper { + position: relative; + overflow: hidden; + height: 100%; +} + +.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; + } +} + +.baron._scrolling > .baron__track .baron__bar { + opacity: 0.6; +} + +.baron__control { + display: none; +} + +.baron.panel-content--scrollable { + // Width needs to be set to prevent content width issues + width: 100%; + + .baron__scroller { + padding-top: 1px; + } +} diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index 47d4a926968..ba37d7ce98f 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -61,6 +61,8 @@ display: flex; flex-direction: column; flex-grow: 1; + + // overflow-y: scroll; } .search-dropdown__col_2 { @@ -99,6 +101,11 @@ } } +.search-results-scroller { + position: relative; + height: 100%; +} + .search-results-container { height: 100%; display: block; diff --git a/public/views/index.template.html b/public/views/index.template.html index 2d408f70f8c..4cb4a0d57ce 100644 --- a/public/views/index.template.html +++ b/public/views/index.template.html @@ -16,7 +16,7 @@ - + @@ -40,6 +40,7 @@
+
From efaf267deba548189e89e04a7557d48ebb46ca04 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 2 Apr 2018 20:47:09 +0300 Subject: [PATCH 03/49] scrollbar: fix legend rendering issues --- public/app/plugins/panel/graph/legend.ts | 19 ++++++------------- public/app/plugins/panel/graph/template.ts | 4 +--- public/sass/components/_panel_graph.scss | 7 ++++--- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/public/app/plugins/panel/graph/legend.ts b/public/app/plugins/panel/graph/legend.ts index 761671c356d..1952f7241e7 100644 --- a/public/app/plugins/panel/graph/legend.ts +++ b/public/app/plugins/panel/graph/legend.ts @@ -238,8 +238,10 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { tbodyElem.append(tableHeaderElem); tbodyElem.append(seriesElements); elem.append(tbodyElem); + tbodyElem.wrap('
'); } else { - elem.append(seriesElements); + elem.append('
'); + elem.find('.graph-legend-content').append(seriesElements); } if (!panel.legend.rightSide || (panel.legend.rightSide && legendWidth !== legendRightDefaultWidth)) { @@ -258,25 +260,16 @@ module.directive('graphLegend', function(popoverSrv, $timeout) {
`; - let scrollRoot = elem.parent(); - // let scroller = elem.find(':first-child').first(); - let scroller = elem; + let scrollRoot = elem; + let scroller = elem.find('.graph-legend-content'); // clear existing scroll bar track to prevent duplication - elem - .parent() - .find('.baron__track') - .remove(); + scrollRoot.find('.baron__track').remove(); scrollRoot.addClass(scrollRootClass); $(scrollBarHTML).appendTo(scrollRoot); scroller.addClass(scrollerClass); - // Fix .graph-legend-content max-height - // Couldn't find how to do it via CSS - const legendHeight = scrollRoot.height(); - elem.css('max-height', legendHeight); - let scrollbarParams = { root: scrollRoot[0], scroller: scroller[0], diff --git a/public/app/plugins/panel/graph/template.ts b/public/app/plugins/panel/graph/template.ts index c897327fe1a..0b9eb8227df 100644 --- a/public/app/plugins/panel/graph/template.ts +++ b/public/app/plugins/panel/graph/template.ts @@ -3,9 +3,7 @@ var template = `
-
-
-
+
`; diff --git a/public/sass/components/_panel_graph.scss b/public/sass/components/_panel_graph.scss index 5885b769564..83747d6caaf 100644 --- a/public/sass/components/_panel_graph.scss +++ b/public/sass/components/_panel_graph.scss @@ -53,9 +53,11 @@ max-height: 30%; margin: 0; text-align: center; - // padding-top: 6px; + padding-top: 6px; position: relative; + height: 100%; + .popover-content { padding: 0; } @@ -63,7 +65,6 @@ .graph-legend-content { position: relative; - padding-top: 6px; } .graph-legend-icon { @@ -129,9 +130,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; From 7b75b251b12752969bf926334f63e441745e52bd Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 2 Apr 2018 21:15:10 +0300 Subject: [PATCH 04/49] scrollbar: fix Firefox issue (white stripe on the right of scrollbar) --- public/sass/components/_scrollbar.scss | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index 216ff1fa1c4..ec6f64a13e4 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -189,6 +189,10 @@ .baron { display: inline-block; overflow: hidden; + + // 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) + width: 99%; } .baron__clipper { @@ -280,7 +284,8 @@ .baron.panel-content--scrollable { // Width needs to be set to prevent content width issues - width: 100%; + // Set to 99% instead of 100% for fixing Firefox issue (white stripe on the right of scrollbar) + width: 99%; .baron__scroller { padding-top: 1px; From 3fcd2627110e180d3538eda4cc01d82f1c2cef84 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 3 Apr 2018 14:48:48 +0300 Subject: [PATCH 05/49] scrollbar: fix side menu on mobile devices --- public/sass/components/_scrollbar.scss | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index ec6f64a13e4..776c85d96b5 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -192,7 +192,17 @@ // 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) - width: 99%; + min-width: 99%; +} + +// Fix for side menu on mobile devices +.sidemenu-open.sidemenu-open--xs { + .main-view.baron { + min-width: 0%; + } +} +.main-view.baron { + min-width: 99%; } .baron__clipper { From a265c77cf907d3522515d9818d6e77d127e70715 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 4 Apr 2018 14:51:46 +0300 Subject: [PATCH 06/49] scrollbar: fix Firefox scroll position restore --- public/app/features/dashboard/view_state_srv.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/app/features/dashboard/view_state_srv.ts b/public/app/features/dashboard/view_state_srv.ts index 576b8b6fce8..fa471b89989 100644 --- a/public/app/features/dashboard/view_state_srv.ts +++ b/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) { From 6b598f34cdab793228c1ad0d94ecc3a9f85718b1 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 4 Apr 2018 19:00:28 +0300 Subject: [PATCH 07/49] scrollbar: fix 'legendScrollbar.destroy is not a function' error --- public/app/plugins/panel/graph/legend.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/panel/graph/legend.ts b/public/app/plugins/panel/graph/legend.ts index 1952f7241e7..c4ff5b31355 100644 --- a/public/app/plugins/panel/graph/legend.ts +++ b/public/app/plugins/panel/graph/legend.ts @@ -19,7 +19,7 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { scope.$on('$destroy', function() { if (legendScrollbar) { - legendScrollbar.destroy(); + legendScrollbar.dispose(); } }); From 4df4249aeadf16247350685eb92ec64f875f56b6 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 4 Apr 2018 19:21:17 +0300 Subject: [PATCH 08/49] scrollbar: fix dashboard width bug --- public/app/core/components/scroll/scroll.ts | 12 ++++++++++-- public/sass/components/_scrollbar.scss | 7 +------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/public/app/core/components/scroll/scroll.ts b/public/app/core/components/scroll/scroll.ts index ad8b4ed3d8e..0eb1c68ab71 100644 --- a/public/app/core/components/scroll/scroll.ts +++ b/public/app/core/components/scroll/scroll.ts @@ -27,14 +27,17 @@ export function geminiScrollbar() { $(scrollBarHTML).appendTo(scrollRoot); elem.addClass(scrollerClass); - let scrollbar = baron({ + 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; @@ -57,6 +60,11 @@ export function geminiScrollbar() { scope ); + appEvents.on('toggle-sidemenu', evt => { + // force updating dashboard width + scrollbar.scroll(); + }); + scope.$on('$routeChangeSuccess', () => { lastPos = 0; elem[0].scrollTop = 0; diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index 776c85d96b5..7fb8ac6608c 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -196,13 +196,8 @@ } // Fix for side menu on mobile devices -.sidemenu-open.sidemenu-open--xs { - .main-view.baron { - min-width: 0%; - } -} .main-view.baron { - min-width: 99%; + min-width: unset; } .baron__clipper { From 175937a679393080d0f4276aa8a0090a44ce14c6 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 4 Apr 2018 19:26:29 +0300 Subject: [PATCH 09/49] scrollbar: remove perfect-scrollbar and add baron to package list --- package.json | 1 - yarn.lock | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 724bbffb6fa..e3c0db170b6 100644 --- a/package.json +++ b/package.json @@ -152,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", diff --git a/yarn.lock b/yarn.lock index 2de60f03c27..1cc9ccc869c 100644 --- a/yarn.lock +++ b/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" From b69316752a4c27b1a9011d0a0e81b36b6cc76d7e Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 4 Apr 2018 19:51:12 +0300 Subject: [PATCH 10/49] scrollbar: fix dashboard width updating for different modes --- public/app/core/components/grafana_app.ts | 1 + public/app/core/components/scroll/scroll.ts | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/public/app/core/components/grafana_app.ts b/public/app/core/components/grafana_app.ts index 798a40cb1bf..1e3f0cb9119 100644 --- a/public/app/core/components/grafana_app.ts +++ b/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); diff --git a/public/app/core/components/scroll/scroll.ts b/public/app/core/components/scroll/scroll.ts index 0eb1c68ab71..ebe105a4fee 100644 --- a/public/app/core/components/scroll/scroll.ts +++ b/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); + appEvents.on('toggle-sidemenu-hidden', forceUpdate); + appEvents.on('toggle-view-mode', forceUpdate); + appEvents.on('toggle-kiosk-mode', forceUpdate); + appEvents.on('toggle-inactive-mode', forceUpdate); + + function forceUpdate() { scrollbar.scroll(); - }); + } scope.$on('$routeChangeSuccess', () => { lastPos = 0; From b4ef55f5d027014a43404658581409b4c8d82f63 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 5 Apr 2018 11:51:32 +0300 Subject: [PATCH 11/49] scrollbar: fix potential memory leaks in event handlers --- public/app/core/components/scroll/scroll.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/app/core/components/scroll/scroll.ts b/public/app/core/components/scroll/scroll.ts index ebe105a4fee..12321bc4f5c 100644 --- a/public/app/core/components/scroll/scroll.ts +++ b/public/app/core/components/scroll/scroll.ts @@ -61,11 +61,11 @@ export function geminiScrollbar() { ); // force updating dashboard width - appEvents.on('toggle-sidemenu', forceUpdate); - appEvents.on('toggle-sidemenu-hidden', forceUpdate); - appEvents.on('toggle-view-mode', forceUpdate); - appEvents.on('toggle-kiosk-mode', forceUpdate); - appEvents.on('toggle-inactive-mode', forceUpdate); + 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(); From 7083e8a0a921cb8ab2013f733351cbc796010b71 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 5 Apr 2018 14:09:32 +0200 Subject: [PATCH 12/49] migrated segment_srv to ts --- public/app/core/services/segment_srv.js | 111 ------------------------ public/app/core/services/segment_srv.ts | 111 ++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 111 deletions(-) delete mode 100644 public/app/core/services/segment_srv.js create mode 100644 public/app/core/services/segment_srv.ts diff --git a/public/app/core/services/segment_srv.js b/public/app/core/services/segment_srv.js deleted file mode 100644 index 71d0cbfe7a9..00000000000 --- a/public/app/core/services/segment_srv.js +++ /dev/null @@ -1,111 +0,0 @@ -define([ - 'angular', - 'lodash', - '../core_module', -], -function (angular, _, coreModule) { - 'use strict'; - - coreModule.default.service('uiSegmentSrv', function($sce, templateSrv) { - var self = this; - - function MetricSegment(options) { - if (options === '*' || options.value === '*') { - this.value = '*'; - this.html = $sce.trustAsHtml(''); - this.type = options.type; - this.expandable = true; - return; - } - - if (_.isString(options)) { - this.value = options; - this.html = $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value)); - return; - } - - // temp hack to work around legacy inconsistency in segment model - this.text = options.value; - - this.cssClass = options.cssClass; - this.custom = options.custom; - this.type = options.type; - this.fake = options.fake; - this.value = options.value; - this.selectMode = options.selectMode; - this.type = options.type; - this.expandable = options.expandable; - this.html = options.html || $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value)); - } - - this.getSegmentForValue = function(value, fallbackText) { - if (value) { - return this.newSegment(value); - } else { - return this.newSegment({value: fallbackText, fake: true}); - } - }; - - this.newSelectMeasurement = function() { - return new MetricSegment({value: 'select measurement', fake: true}); - }; - - this.newFake = function(text, type, cssClass) { - return new MetricSegment({value: text, fake: true, type: type, cssClass: cssClass}); - }; - - this.newSegment = function(options) { - return new MetricSegment(options); - }; - - this.newKey = function(key) { - return new MetricSegment({value: key, type: 'key', cssClass: 'query-segment-key' }); - }; - - this.newKeyValue = function(value) { - return new MetricSegment({value: value, type: 'value', cssClass: 'query-segment-value' }); - }; - - this.newCondition = function(condition) { - return new MetricSegment({value: condition, type: 'condition', cssClass: 'query-keyword' }); - }; - - this.newOperator = function(op) { - return new MetricSegment({value: op, type: 'operator', cssClass: 'query-segment-operator' }); - }; - - this.newOperators = function(ops) { - return _.map(ops, function(op) { - return new MetricSegment({value: op, type: 'operator', cssClass: 'query-segment-operator' }); - }); - }; - - this.transformToSegments = function(addTemplateVars, variableTypeFilter) { - return function(results) { - var segments = _.map(results, function(segment) { - return self.newSegment({value: segment.text, expandable: segment.expandable}); - }); - - if (addTemplateVars) { - _.each(templateSrv.variables, function(variable) { - if (variableTypeFilter === void 0 || variableTypeFilter === variable.type) { - segments.unshift(self.newSegment({ type: 'value', value: '$' + variable.name, expandable: true })); - } - }); - } - - return segments; - }; - }; - - this.newSelectMetric = function() { - return new MetricSegment({value: 'select metric', fake: true}); - }; - - this.newPlusButton = function() { - return new MetricSegment({fake: true, html: '', type: 'plus-button', cssClass: 'query-part' }); - }; - - }); - -}); diff --git a/public/app/core/services/segment_srv.ts b/public/app/core/services/segment_srv.ts new file mode 100644 index 00000000000..042340e6102 --- /dev/null +++ b/public/app/core/services/segment_srv.ts @@ -0,0 +1,111 @@ +import _ from 'lodash'; +import coreModule from '../core_module'; + +/** @ngInject */ +export function uiSegmentSrv($sce, templateSrv) { + let self = this; + + function MetricSegment(options) { + if (options === '*' || options.value === '*') { + this.value = '*'; + this.html = $sce.trustAsHtml(''); + this.type = options.type; + this.expandable = true; + return; + } + + if (_.isString(options)) { + this.value = options; + this.html = $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value)); + return; + } + + // temp hack to work around legacy inconsistency in segment model + this.text = options.value; + + this.cssClass = options.cssClass; + this.custom = options.custom; + this.type = options.type; + this.fake = options.fake; + this.value = options.value; + this.selectMode = options.selectMode; + this.type = options.type; + this.expandable = options.expandable; + this.html = options.html || $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value)); + } + + this.getSegmentForValue = function(value, fallbackText) { + if (value) { + return this.newSegment(value); + } else { + return this.newSegment({ value: fallbackText, fake: true }); + } + }; + + this.newSelectMeasurement = function() { + return new MetricSegment({ value: 'select measurement', fake: true }); + }; + + this.newFake = function(text, type, cssClass) { + return new MetricSegment({ value: text, fake: true, type: type, cssClass: cssClass }); + }; + + this.newSegment = function(options) { + return new MetricSegment(options); + }; + + this.newKey = function(key) { + return new MetricSegment({ value: key, type: 'key', cssClass: 'query-segment-key' }); + }; + + this.newKeyValue = function(value) { + return new MetricSegment({ value: value, type: 'value', cssClass: 'query-segment-value' }); + }; + + this.newCondition = function(condition) { + return new MetricSegment({ value: condition, type: 'condition', cssClass: 'query-keyword' }); + }; + + this.newOperator = function(op) { + return new MetricSegment({ value: op, type: 'operator', cssClass: 'query-segment-operator' }); + }; + + this.newOperators = function(ops) { + return _.map(ops, function(op) { + return new MetricSegment({ value: op, type: 'operator', cssClass: 'query-segment-operator' }); + }); + }; + + this.transformToSegments = function(addTemplateVars, variableTypeFilter) { + return function(results) { + let segments = _.map(results, function(segment) { + return self.newSegment({ value: segment.text, expandable: segment.expandable }); + }); + + if (addTemplateVars) { + _.each(templateSrv.variables, function(variable) { + if (variableTypeFilter === void 0 || variableTypeFilter === variable.type) { + segments.unshift(self.newSegment({ type: 'value', value: '$' + variable.name, expandable: true })); + } + }); + } + + return segments; + }; + }; + + this.newSelectMetric = function() { + return new MetricSegment({ value: 'select metric', fake: true }); + }; + + this.newPlusButton = function() { + return new MetricSegment({ + fake: true, + html: '', + type: 'plus-button', + cssClass: 'query-part', + }); + }; +} + +coreModule.service('uiSegmentSrv', uiSegmentSrv); From 714d555e366f0dd752a8b1630b99be7a4187ef5b Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 5 Apr 2018 14:33:27 +0200 Subject: [PATCH 13/49] migrated dash_class to ts --- public/app/core/directives/dash_class.js | 36 ------------------------ public/app/core/directives/dash_class.ts | 31 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 36 deletions(-) delete mode 100644 public/app/core/directives/dash_class.js create mode 100644 public/app/core/directives/dash_class.ts diff --git a/public/app/core/directives/dash_class.js b/public/app/core/directives/dash_class.js deleted file mode 100644 index 4a139272632..00000000000 --- a/public/app/core/directives/dash_class.js +++ /dev/null @@ -1,36 +0,0 @@ -define([ - 'lodash', - 'jquery', - '../core_module', -], -function (_, $, coreModule) { - 'use strict'; - - coreModule.default.directive('dashClass', function() { - return { - link: function($scope, elem) { - - $scope.onAppEvent('panel-fullscreen-enter', function() { - elem.toggleClass('panel-in-fullscreen', true); - }); - - $scope.onAppEvent('panel-fullscreen-exit', function() { - elem.toggleClass('panel-in-fullscreen', false); - }); - - $scope.$watch('ctrl.dashboardViewState.state.editview', function(newValue) { - if (newValue) { - elem.toggleClass('dashboard-page--settings-opening', _.isString(newValue)); - setTimeout(function() { - elem.toggleClass('dashboard-page--settings-open', _.isString(newValue)); - }, 10); - } else { - elem.removeClass('dashboard-page--settings-opening'); - elem.removeClass('dashboard-page--settings-open'); - } - }); - } - }; - }); - -}); diff --git a/public/app/core/directives/dash_class.ts b/public/app/core/directives/dash_class.ts new file mode 100644 index 00000000000..031338d3c5b --- /dev/null +++ b/public/app/core/directives/dash_class.ts @@ -0,0 +1,31 @@ +import _ from 'lodash'; +import coreModule from '../core_module'; + +/** @ngInject */ +export function dashClass() { + return { + link: function($scope, elem) { + $scope.onAppEvent('panel-fullscreen-enter', function() { + elem.toggleClass('panel-in-fullscreen', true); + }); + + $scope.onAppEvent('panel-fullscreen-exit', function() { + elem.toggleClass('panel-in-fullscreen', false); + }); + + $scope.$watch('ctrl.dashboardViewState.state.editview', function(newValue) { + if (newValue) { + elem.toggleClass('dashboard-page--settings-opening', _.isString(newValue)); + setTimeout(function() { + elem.toggleClass('dashboard-page--settings-open', _.isString(newValue)); + }, 10); + } else { + elem.removeClass('dashboard-page--settings-opening'); + elem.removeClass('dashboard-page--settings-open'); + } + }); + }, + }; +} + +coreModule.directive('dashClass', dashClass); From fa43782299a6fde669d9ecd186eb1aa094db0067 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 5 Apr 2018 15:56:07 +0300 Subject: [PATCH 14/49] scrollbar: fix graph legend height --- public/app/plugins/panel/graph/legend.ts | 8 ++++---- public/app/plugins/panel/graph/template.ts | 4 +++- public/sass/components/_panel_graph.scss | 8 +++++++- public/sass/components/_scrollbar.scss | 4 ++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/public/app/plugins/panel/graph/legend.ts b/public/app/plugins/panel/graph/legend.ts index c4ff5b31355..d3b548859de 100644 --- a/public/app/plugins/panel/graph/legend.ts +++ b/public/app/plugins/panel/graph/legend.ts @@ -238,10 +238,10 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { tbodyElem.append(tableHeaderElem); tbodyElem.append(seriesElements); elem.append(tbodyElem); - tbodyElem.wrap('
'); + tbodyElem.wrap('
'); } else { - elem.append('
'); - elem.find('.graph-legend-content').append(seriesElements); + elem.append('
'); + elem.find('.graph-legend-scroll').append(seriesElements); } if (!panel.legend.rightSide || (panel.legend.rightSide && legendWidth !== legendRightDefaultWidth)) { @@ -261,7 +261,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(); diff --git a/public/app/plugins/panel/graph/template.ts b/public/app/plugins/panel/graph/template.ts index 0b9eb8227df..c897327fe1a 100644 --- a/public/app/plugins/panel/graph/template.ts +++ b/public/app/plugins/panel/graph/template.ts @@ -3,7 +3,9 @@ var template = `
-
+
+
+
`; diff --git a/public/sass/components/_panel_graph.scss b/public/sass/components/_panel_graph.scss index 83747d6caaf..cc5c601e0ea 100644 --- a/public/sass/components/_panel_graph.scss +++ b/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; diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index 7fb8ac6608c..93b81fc1b4b 100644 --- a/public/sass/components/_scrollbar.scss +++ b/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 { From 9f07ae72ee4bebc41ec06bc85793529bf045aa66 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 5 Apr 2018 16:47:20 +0300 Subject: [PATCH 15/49] scrollbar: fix search scroller in mobile view --- public/sass/components/_search.scss | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index f54ac4b3d5c..8338a5d72ae 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -103,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; From 35bc4e4632a1195b59e8b52cf0e969a08f24e470 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 5 Apr 2018 17:10:32 +0200 Subject: [PATCH 16/49] migrated metric_segment to ts --- public/app/core/directives/metric_segment.js | 246 ----------------- public/app/core/directives/metric_segment.ts | 261 +++++++++++++++++++ 2 files changed, 261 insertions(+), 246 deletions(-) delete mode 100644 public/app/core/directives/metric_segment.js create mode 100644 public/app/core/directives/metric_segment.ts diff --git a/public/app/core/directives/metric_segment.js b/public/app/core/directives/metric_segment.js deleted file mode 100644 index 7ba4a5a5259..00000000000 --- a/public/app/core/directives/metric_segment.js +++ /dev/null @@ -1,246 +0,0 @@ -define([ - 'lodash', - 'jquery', - '../core_module', -], -function (_, $, coreModule) { - 'use strict'; - - coreModule.default.directive('metricSegment', function($compile, $sce) { - var inputTemplate = ''; - - var linkTemplate = ''; - - var selectTemplate = ''; - - return { - scope: { - segment: "=", - getOptions: "&", - onChange: "&", - debounce: "@", - }, - link: function($scope, elem) { - var $input = $(inputTemplate); - var segment = $scope.segment; - var $button = $(segment.selectMode ? selectTemplate : linkTemplate); - var options = null; - var cancelBlur = null; - var linkMode = true; - var debounceLookup = $scope.debounce; - - $input.appendTo(elem); - $button.appendTo(elem); - - $scope.updateVariableValue = function(value) { - if (value === '' || segment.value === value) { - return; - } - - value = _.unescape(value); - - $scope.$apply(function() { - var selected = _.find($scope.altSegments, {value: value}); - if (selected) { - segment.value = selected.value; - segment.html = selected.html || selected.value; - segment.fake = false; - segment.expandable = selected.expandable; - - if (selected.type) { - segment.type = selected.type; - } - } - else if (segment.custom !== 'false') { - segment.value = value; - segment.html = $sce.trustAsHtml(value); - segment.expandable = true; - segment.fake = false; - } - - $scope.onChange(); - }); - }; - - $scope.switchToLink = function(fromClick) { - if (linkMode && !fromClick) { return; } - - clearTimeout(cancelBlur); - cancelBlur = null; - linkMode = true; - $input.hide(); - $button.show(); - $scope.updateVariableValue($input.val()); - }; - - $scope.inputBlur = function() { - // happens long before the click event on the typeahead options - // need to have long delay because the blur - cancelBlur = setTimeout($scope.switchToLink, 200); - }; - - $scope.source = function(query, callback) { - $scope.$apply(function() { - $scope.getOptions({ $query: query }).then(function(altSegments) { - $scope.altSegments = altSegments; - options = _.map($scope.altSegments, function(alt) { - return _.escape(alt.value); - }); - - // add custom values - if (segment.custom !== 'false') { - if (!segment.fake && _.indexOf(options, segment.value) === -1) { - options.unshift(segment.value); - } - } - - callback(options); - }); - }); - }; - - $scope.updater = function(value) { - if (value === segment.value) { - clearTimeout(cancelBlur); - $input.focus(); - return value; - } - - $input.val(value); - $scope.switchToLink(true); - - return value; - }; - - $scope.matcher = function(item) { - var str = this.query; - if (str[0] === '/') { str = str.substring(1); } - if (str[str.length - 1] === '/') { str = str.substring(0, str.length-1); } - try { - return item.toLowerCase().match(str.toLowerCase()); - } catch(e) { - return false; - } - }; - - $input.attr('data-provide', 'typeahead'); - $input.typeahead({ source: $scope.source, minLength: 0, items: 10000, updater: $scope.updater, matcher: $scope.matcher }); - - var typeahead = $input.data('typeahead'); - typeahead.lookup = function () { - this.query = this.$element.val() || ''; - var items = this.source(this.query, $.proxy(this.process, this)); - return items ? this.process(items) : items; - }; - - if (debounceLookup) { - typeahead.lookup = _.debounce(typeahead.lookup, 500, {leading: true}); - } - - $button.keydown(function(evt) { - // trigger typeahead on down arrow or enter key - if (evt.keyCode === 40 || evt.keyCode === 13) { - $button.click(); - } - }); - - $button.click(function() { - options = null; - $input.css('width', (Math.max($button.width(), 80) + 16) + 'px'); - - $button.hide(); - $input.show(); - $input.focus(); - - linkMode = false; - - var typeahead = $input.data('typeahead'); - if (typeahead) { - $input.val(''); - typeahead.lookup(); - } - }); - - $input.blur($scope.inputBlur); - - $compile(elem.contents())($scope); - } - }; - }); - - coreModule.default.directive('metricSegmentModel', function(uiSegmentSrv, $q) { - return { - template: '', - restrict: 'E', - scope: { - property: "=", - options: "=", - getOptions: "&", - onChange: "&", - }, - link: { - pre: function postLink($scope, elem, attrs) { - var cachedOptions; - - $scope.valueToSegment = function(value) { - var option = _.find($scope.options, {value: value}); - var segment = { - cssClass: attrs.cssClass, - custom: attrs.custom, - value: option ? option.text : value, - selectMode: attrs.selectMode, - }; - - return uiSegmentSrv.newSegment(segment); - }; - - $scope.getOptionsInternal = function() { - if ($scope.options) { - cachedOptions = $scope.options; - return $q.when(_.map($scope.options, function(option) { - return {value: option.text}; - })); - } else { - return $scope.getOptions().then(function(options) { - cachedOptions = options; - return _.map(options, function(option) { - if (option.html) { - return option; - } - return {value: option.text}; - }); - }); - } - }; - - $scope.onSegmentChange = function() { - if (cachedOptions) { - var option = _.find(cachedOptions, {text: $scope.segment.value}); - if (option && option.value !== $scope.property) { - $scope.property = option.value; - } else if (attrs.custom !== 'false') { - $scope.property = $scope.segment.value; - } - } else { - $scope.property = $scope.segment.value; - } - - // needs to call this after digest so - // property is synced with outerscope - $scope.$$postDigest(function() { - $scope.$apply(function() { - $scope.onChange(); - }); - }); - }; - - $scope.segment = $scope.valueToSegment($scope.property); - } - } - }; - }); -}); diff --git a/public/app/core/directives/metric_segment.ts b/public/app/core/directives/metric_segment.ts new file mode 100644 index 00000000000..d1054b23793 --- /dev/null +++ b/public/app/core/directives/metric_segment.ts @@ -0,0 +1,261 @@ +import _ from 'lodash'; +import $ from 'jquery'; +import coreModule from '../core_module'; + +export function metricSegment($compile, $sce) { + let inputTemplate = + ''; + + let linkTemplate = + ''; + + let selectTemplate = + ''; + + return { + scope: { + segment: '=', + getOptions: '&', + onChange: '&', + debounce: '@', + }, + link: function($scope, elem) { + let $input = $(inputTemplate); + let segment = $scope.segment; + let $button = $(segment.selectMode ? selectTemplate : linkTemplate); + let options = null; + let cancelBlur = null; + let linkMode = true; + let debounceLookup = $scope.debounce; + + $input.appendTo(elem); + $button.appendTo(elem); + + $scope.updateVariableValue = function(value) { + if (value === '' || segment.value === value) { + return; + } + + value = _.unescape(value); + + $scope.$apply(function() { + let selected = _.find($scope.altSegments, { value: value }); + if (selected) { + segment.value = selected.value; + segment.html = selected.html || selected.value; + segment.fake = false; + segment.expandable = selected.expandable; + + if (selected.type) { + segment.type = selected.type; + } + } else if (segment.custom !== 'false') { + segment.value = value; + segment.html = $sce.trustAsHtml(value); + segment.expandable = true; + segment.fake = false; + } + + $scope.onChange(); + }); + }; + + $scope.switchToLink = function(fromClick) { + if (linkMode && !fromClick) { + return; + } + + clearTimeout(cancelBlur); + cancelBlur = null; + linkMode = true; + $input.hide(); + $button.show(); + $scope.updateVariableValue($input.val()); + }; + + $scope.inputBlur = function() { + // happens long before the click event on the typeahead options + // need to have long delay because the blur + cancelBlur = setTimeout($scope.switchToLink, 200); + }; + + $scope.source = function(query, callback) { + $scope.$apply(function() { + $scope.getOptions({ $query: query }).then(function(altSegments) { + $scope.altSegments = altSegments; + options = _.map($scope.altSegments, function(alt) { + return _.escape(alt.value); + }); + + // add custom values + if (segment.custom !== 'false') { + if (!segment.fake && _.indexOf(options, segment.value) === -1) { + options.unshift(segment.value); + } + } + + callback(options); + }); + }); + }; + + $scope.updater = function(value) { + if (value === segment.value) { + clearTimeout(cancelBlur); + $input.focus(); + return value; + } + + $input.val(value); + $scope.switchToLink(true); + + return value; + }; + + $scope.matcher = function(item) { + let str = this.query; + if (str[0] === '/') { + str = str.substring(1); + } + if (str[str.length - 1] === '/') { + str = str.substring(0, str.length - 1); + } + try { + return item.toLowerCase().match(str.toLowerCase()); + } catch (e) { + return false; + } + }; + + $input.attr('data-provide', 'typeahead'); + $input.typeahead({ + source: $scope.source, + minLength: 0, + items: 10000, + updater: $scope.updater, + matcher: $scope.matcher, + }); + + let typeahead = $input.data('typeahead'); + typeahead.lookup = function() { + this.query = this.$element.val() || ''; + let items = this.source(this.query, $.proxy(this.process, this)); + return items ? this.process(items) : items; + }; + + if (debounceLookup) { + typeahead.lookup = _.debounce(typeahead.lookup, 500, { leading: true }); + } + + $button.keydown(function(evt) { + // trigger typeahead on down arrow or enter key + if (evt.keyCode === 40 || evt.keyCode === 13) { + $button.click(); + } + }); + + $button.click(function() { + options = null; + $input.css('width', Math.max($button.width(), 80) + 16 + 'px'); + + $button.hide(); + $input.show(); + $input.focus(); + + linkMode = false; + + let typeahead = $input.data('typeahead'); + if (typeahead) { + $input.val(''); + typeahead.lookup(); + } + }); + + $input.blur($scope.inputBlur); + + $compile(elem.contents())($scope); + }, + }; +} + +export function metricSegmentModel(uiSegmentSrv, $q) { + return { + template: + '', + restrict: 'E', + scope: { + property: '=', + options: '=', + getOptions: '&', + onChange: '&', + }, + link: { + pre: function postLink($scope, elem, attrs) { + let cachedOptions; + + $scope.valueToSegment = function(value) { + let option = _.find($scope.options, { value: value }); + let segment = { + cssClass: attrs.cssClass, + custom: attrs.custom, + value: option ? option.text : value, + selectMode: attrs.selectMode, + }; + + return uiSegmentSrv.newSegment(segment); + }; + + $scope.getOptionsInternal = function() { + if ($scope.options) { + cachedOptions = $scope.options; + return $q.when( + _.map($scope.options, function(option) { + return { value: option.text }; + }) + ); + } else { + return $scope.getOptions().then(function(options) { + cachedOptions = options; + return _.map(options, function(option) { + if (option.html) { + return option; + } + return { value: option.text }; + }); + }); + } + }; + + $scope.onSegmentChange = function() { + if (cachedOptions) { + let option = _.find(cachedOptions, { text: $scope.segment.value }); + if (option && option.value !== $scope.property) { + $scope.property = option.value; + } else if (attrs.custom !== 'false') { + $scope.property = $scope.segment.value; + } + } else { + $scope.property = $scope.segment.value; + } + + // needs to call this after digest so + // property is synced with outerscope + $scope.$$postDigest(function() { + $scope.$apply(function() { + $scope.onChange(); + }); + }); + }; + + $scope.segment = $scope.valueToSegment($scope.property); + }, + }, + }; +} + +coreModule.directive('metricSegment', metricSegment); +coreModule.directive('metricSegmentModel', metricSegmentModel); From 6d6ecbd458a841d226bf308990b47affcca474cd Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Fri, 6 Apr 2018 10:54:06 +0200 Subject: [PATCH 17/49] fixed sidemenu icon issue created by earlier pr --- public/sass/base/_icons.scss | 6 ++---- public/sass/components/_sidemenu.scss | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/sass/base/_icons.scss b/public/sass/base/_icons.scss index 31e5ee62d6f..c701cc1249e 100644 --- a/public/sass/base/_icons.scss +++ b/public/sass/base/_icons.scss @@ -1,10 +1,8 @@ .gicon { line-height: 1; display: inline-block; - //width: 1.1057142857em; - //height: 1.1057142857em; - height: 22px; - width: 22px; + width: 1.1057142857em; + height: 1.1057142857em; text-align: center; background-repeat: no-repeat; background-position: center; diff --git a/public/sass/components/_sidemenu.scss b/public/sass/components/_sidemenu.scss index e48ab0597a2..d1372484074 100644 --- a/public/sass/components/_sidemenu.scss +++ b/public/sass/components/_sidemenu.scss @@ -123,6 +123,8 @@ position: relative; opacity: 0.7; font-size: 130%; + height: 22px; + width: 22px; } .fa { From d3d64337b48969d7f7367f8a5122ac3410833961 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Fri, 6 Apr 2018 20:00:03 +0300 Subject: [PATCH 18/49] scrollbar: styles cleanup --- public/app/core/components/scroll/scroll.ts | 1 - public/app/features/panel/panel_directive.ts | 2 ++ public/app/plugins/panel/graph/legend.ts | 5 ++--- public/sass/components/_panel_graph.scss | 5 ++++- public/sass/components/_scrollbar.scss | 9 ++------- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/public/app/core/components/scroll/scroll.ts b/public/app/core/components/scroll/scroll.ts index 12321bc4f5c..3f9865e6dce 100644 --- a/public/app/core/components/scroll/scroll.ts +++ b/public/app/core/components/scroll/scroll.ts @@ -77,7 +77,6 @@ export function geminiScrollbar() { }); scope.$on('$destroy', () => { - // scrollbar.destroy(); scrollbar.dispose(); }); }, diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index 4edf293c801..90ff42f4ac6 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -125,6 +125,8 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) { barOnCls: '_scrollbar', scrollingCls: '_scrolling', }); + + panelScrollbar.scroll(); } }); diff --git a/public/app/plugins/panel/graph/legend.ts b/public/app/plugins/panel/graph/legend.ts index f61df4d2683..752dc591147 100644 --- a/public/app/plugins/panel/graph/legend.ts +++ b/public/app/plugins/panel/graph/legend.ts @@ -18,9 +18,7 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { const legendRightDefaultWidth = 10; scope.$on('$destroy', function() { - if (legendScrollbar) { - legendScrollbar.dispose(); - } + destroyScrollbar(); }); ctrl.events.on('render-legend', () => { @@ -288,6 +286,7 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { destroyScrollbar(); legendScrollbar = baron(scrollbarParams); } + legendScrollbar.scroll(); } function destroyScrollbar() { diff --git a/public/sass/components/_panel_graph.scss b/public/sass/components/_panel_graph.scss index cc5c601e0ea..031192dd984 100644 --- a/public/sass/components/_panel_graph.scss +++ b/public/sass/components/_panel_graph.scss @@ -58,7 +58,7 @@ position: relative; // fix for Firefox (white stripe on the right of scrollbar) - width: 99%; + width: calc(100% - 1px); .popover-content { padding: 0; @@ -67,6 +67,9 @@ .graph-legend-content { position: relative; + + // fix for Firefox (white stripe on the right of scrollbar) + width: calc(100% - 1px); } .graph-legend-scroll { diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index 93b81fc1b4b..51e061e54b9 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -189,10 +189,6 @@ .baron { display: inline-block; overflow: hidden; - - // 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) - width: 99%; } // Fix for side menu on mobile devices @@ -203,7 +199,6 @@ .baron__clipper { position: relative; overflow: hidden; - height: 100%; } .baron__scroller { @@ -289,8 +284,8 @@ .baron.panel-content--scrollable { // 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) - width: 99%; + // 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; From 41e5d66e399ea096bea99b5922722eb3c08b92b6 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Fri, 6 Apr 2018 20:31:44 +0300 Subject: [PATCH 19/49] scrollbar: fix add panel height bug --- public/app/features/dashboard/dashgrid/AddPanelPanel.tsx | 2 +- public/sass/components/_panel_add_panel.scss | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/public/app/features/dashboard/dashgrid/AddPanelPanel.tsx b/public/app/features/dashboard/dashgrid/AddPanelPanel.tsx index aeb840c317a..98d1657f4bd 100644 --- a/public/app/features/dashboard/dashgrid/AddPanelPanel.tsx +++ b/public/app/features/dashboard/dashgrid/AddPanelPanel.tsx @@ -103,7 +103,7 @@ export class AddPanelPanel extends React.Component +
diff --git a/public/sass/components/_panel_add_panel.scss b/public/sass/components/_panel_add_panel.scss index ee5d757d841..4a17438ffeb 100644 --- a/public/sass/components/_panel_add_panel.scss +++ b/public/sass/components/_panel_add_panel.scss @@ -1,3 +1,7 @@ +.add-panel-container { + height: 100%; +} + .add-panel { height: 100%; From ce787b88bf1525c555137f85d405cb0b481485b8 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 6 Apr 2018 21:29:42 +0200 Subject: [PATCH 20/49] css: quick fix after IE11 changes Temporary fix so that the add panel panel works properly. This will break a3f15ced6805d3d949d02c5a8a1d5267a6dac3a7. --- public/sass/pages/_dashboard.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/sass/pages/_dashboard.scss b/public/sass/pages/_dashboard.scss index c957b6af790..871db4dfc2d 100644 --- a/public/sass/pages/_dashboard.scss +++ b/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; From d60c7b201316268725124fe113bb4ea27ab50e16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 9 Apr 2018 08:18:58 +0200 Subject: [PATCH 21/49] docs: updated debian distro in install docs to stretch, closes #11527 --- docs/sources/installation/debian.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sources/installation/debian.md b/docs/sources/installation/debian.md index 27c0e41ac15..8b51a75a826 100644 --- a/docs/sources/installation/debian.md +++ b/docs/sources/installation/debian.md @@ -34,7 +34,7 @@ sudo dpkg -i grafana_5.0.4_amd64.deb Add the following line to your `/etc/apt/sources.list` file. ```bash -deb https://packagecloud.io/grafana/stable/debian/ jessie main +deb https://packagecloud.io/grafana/stable/debian/ stretch main ``` Use the above line even if you are on Ubuntu or another Debian version. @@ -42,7 +42,7 @@ There is also a testing repository if you want beta or release candidates. ```bash -deb https://packagecloud.io/grafana/testing/debian/ jessie main +deb https://packagecloud.io/grafana/testing/debian/ stretch main ``` Then add the [Package Cloud](https://packagecloud.io/grafana) key. This From 25be937bfa2f8ede81323079a33a8011289d5522 Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 9 Apr 2018 09:55:10 +0200 Subject: [PATCH 22/49] docs: fixes typo --- docs/sources/features/datasources/cloudwatch.md | 2 +- docs/sources/features/datasources/elasticsearch.md | 2 +- docs/sources/features/datasources/graphite.md | 2 +- docs/sources/features/datasources/influxdb.md | 2 +- docs/sources/features/datasources/mysql.md | 2 +- docs/sources/features/datasources/opentsdb.md | 2 +- docs/sources/features/datasources/postgres.md | 2 +- docs/sources/features/datasources/prometheus.md | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/sources/features/datasources/cloudwatch.md b/docs/sources/features/datasources/cloudwatch.md index 3013f6b1fc9..bd668bfe93c 100644 --- a/docs/sources/features/datasources/cloudwatch.md +++ b/docs/sources/features/datasources/cloudwatch.md @@ -176,7 +176,7 @@ Grafana will issue a ListMetrics request. ## Configure datasource with provisioning -Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). Here is an example of how you can configure the Cloudwatch datasource using configuration. Using a credentials file diff --git a/docs/sources/features/datasources/elasticsearch.md b/docs/sources/features/datasources/elasticsearch.md index 2cfe74588d1..8acc40f87c2 100644 --- a/docs/sources/features/datasources/elasticsearch.md +++ b/docs/sources/features/datasources/elasticsearch.md @@ -140,7 +140,7 @@ Tags | Optional field name to use for event tags (can be an array or a CSV strin ## Configure datasource with provisioning -Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/graphite.md b/docs/sources/features/datasources/graphite.md index 040a21e5d0c..fb2c6f0bd52 100644 --- a/docs/sources/features/datasources/graphite.md +++ b/docs/sources/features/datasources/graphite.md @@ -123,7 +123,7 @@ specify a tag or wildcard (leave empty should also work) ## Configure datasource with provisioning -Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/influxdb.md b/docs/sources/features/datasources/influxdb.md index e36adb3784e..6b0d92c8e6f 100644 --- a/docs/sources/features/datasources/influxdb.md +++ b/docs/sources/features/datasources/influxdb.md @@ -177,7 +177,7 @@ Tags field can be a comma separated string. ## Configure datasource with provisioning -Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/mysql.md b/docs/sources/features/datasources/mysql.md index dc05b0b31de..3b6daed622f 100644 --- a/docs/sources/features/datasources/mysql.md +++ b/docs/sources/features/datasources/mysql.md @@ -228,7 +228,7 @@ Time series queries should work in alerting conditions. Table formatted queries ## Configure datasource with provisioning -Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/opentsdb.md b/docs/sources/features/datasources/opentsdb.md index f67861e2a51..a233d4fefe8 100644 --- a/docs/sources/features/datasources/opentsdb.md +++ b/docs/sources/features/datasources/opentsdb.md @@ -91,7 +91,7 @@ For details on OpenTSDB metric queries checkout the official [OpenTSDB documenta ## Configure datasource with provisioning -Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/postgres.md b/docs/sources/features/datasources/postgres.md index b70ab1cde04..6384d101dff 100644 --- a/docs/sources/features/datasources/postgres.md +++ b/docs/sources/features/datasources/postgres.md @@ -220,7 +220,7 @@ conditions. ## Configure datasource with provisioning -Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/prometheus.md b/docs/sources/features/datasources/prometheus.md index ebd9ce32a47..ba0ef626745 100644 --- a/docs/sources/features/datasources/prometheus.md +++ b/docs/sources/features/datasources/prometheus.md @@ -103,7 +103,7 @@ Since 4.6.0 Grafana exposes metrics for Prometheus on the `/metrics` endpoint. W ## Configure datasource with provisioning -Its now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources +It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml From 95132165f85c4309c6932aacbe49b58ba5ed13e3 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Mon, 9 Apr 2018 12:47:19 +0200 Subject: [PATCH 23/49] data source: rename direct/proxy access mode in data source settings Changes access drop down options names: - proxy -> Server (Default) - direct -> Browser Replace access drop down info icon/tooltip with expandable/collapsable help section. --- .../plugins/partials/ds_http_settings.html | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/public/app/features/plugins/partials/ds_http_settings.html b/public/app/features/plugins/partials/ds_http_settings.html index 03df677ba13..b9f5683129c 100644 --- a/public/app/features/plugins/partials/ds_http_settings.html +++ b/public/app/features/plugins/partials/ds_http_settings.html @@ -1,5 +1,3 @@ - -

HTTP

@@ -13,12 +11,12 @@

Specify a complete HTTP URL (for example http://your_server:8080)

- Your access method is Direct, this means the URL + Your access method is Browser, this means the URL needs to be accessible from the browser. - Your access method is currently Proxy, this means the URL - needs to be accessible from the grafana backend. + Your access method is Server, this means the URL + needs to be accessible from the grafana backend/server.
@@ -27,14 +25,38 @@
Access -
- - - Direct = URL is used directly from browser
- Proxy = Grafana backend will proxy the request -
+
+
+
+ +
+
+ +
+
+

+ Access mode controls how requests to the data source will be handled. + Server should be the preferred way if nothing else stated. +

+
Server access mode (Default):
+

+ All requests will be made from the browser to Grafana backend/server which in turn will forward the requests to the data source + and by that circumvent possible Cross-Origin Resource Sharing (CORS) requirements. + The URL needs to be accessible from the grafana backend/server if you select this access mode. +

+
Browser access mode:
+

+ All requests will be made from the browser directly to the data source and may be subject to + Cross-Origin Resource Sharing (CORS) requirements. The URL needs to be accessible from the browser if you select this + access mode. +

+
@@ -135,4 +157,3 @@
- From 00524e68273d83566f9312add6d653f4a870b012 Mon Sep 17 00:00:00 2001 From: Sven Klemm <31455525+svenklemm@users.noreply.github.com> Date: Mon, 9 Apr 2018 12:51:17 +0200 Subject: [PATCH 24/49] prevent angular from evaluating {{hostname}} in tooltip (#11514) --- .../plugins/datasource/prometheus/partials/query.editor.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/prometheus/partials/query.editor.html b/public/app/plugins/datasource/prometheus/partials/query.editor.html index 8d6e89c3406..68791d96c19 100644 --- a/public/app/plugins/datasource/prometheus/partials/query.editor.html +++ b/public/app/plugins/datasource/prometheus/partials/query.editor.html @@ -14,7 +14,7 @@ data-min-length=0 data-items=1000 ng-model-onblur ng-change="ctrl.refreshMetricData()"> - Controls the name of the time series, using name or pattern. For example {{hostname}} will be replaced with label value for + Controls the name of the time series, using name or pattern. For example {{hostname}} will be replaced with label value for the label hostname.
From d9ba16f550afca4112fcab6a0641f164608e91d5 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 9 Apr 2018 14:21:20 +0300 Subject: [PATCH 25/49] scrollbar: fix phantomjs rendering error --- public/sass/components/_scrollbar.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index 51e061e54b9..4bd0807bb0b 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -187,7 +187,7 @@ // Baron styles .baron { - display: inline-block; + // display: inline-block; // this brakes phantomjs rendering (width becomes 0) overflow: hidden; } From 2889a405a774fed0cebc168bb2894f0c747c2727 Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 9 Apr 2018 14:56:42 +0200 Subject: [PATCH 26/49] docs: improves provisoning example text --- docs/sources/features/datasources/cloudwatch.md | 5 +++-- docs/sources/features/datasources/elasticsearch.md | 3 ++- docs/sources/features/datasources/graphite.md | 3 ++- docs/sources/features/datasources/influxdb.md | 3 ++- docs/sources/features/datasources/mysql.md | 3 ++- docs/sources/features/datasources/opentsdb.md | 3 ++- docs/sources/features/datasources/postgres.md | 3 ++- docs/sources/features/datasources/prometheus.md | 3 ++- 8 files changed, 17 insertions(+), 9 deletions(-) diff --git a/docs/sources/features/datasources/cloudwatch.md b/docs/sources/features/datasources/cloudwatch.md index bd668bfe93c..3aa310eaade 100644 --- a/docs/sources/features/datasources/cloudwatch.md +++ b/docs/sources/features/datasources/cloudwatch.md @@ -176,8 +176,9 @@ Grafana will issue a ListMetrics request. ## Configure datasource with provisioning -It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). -Here is an example of how you can configure the Cloudwatch datasource using configuration. +It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) + +Here are some examples of how you can configure the Cloudwatch datasource using configuration. Using a credentials file ```yaml diff --git a/docs/sources/features/datasources/elasticsearch.md b/docs/sources/features/datasources/elasticsearch.md index 8acc40f87c2..c0b5f4b9283 100644 --- a/docs/sources/features/datasources/elasticsearch.md +++ b/docs/sources/features/datasources/elasticsearch.md @@ -140,7 +140,8 @@ Tags | Optional field name to use for event tags (can be an array or a CSV strin ## Configure datasource with provisioning -It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) + Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/graphite.md b/docs/sources/features/datasources/graphite.md index fb2c6f0bd52..895664b09ad 100644 --- a/docs/sources/features/datasources/graphite.md +++ b/docs/sources/features/datasources/graphite.md @@ -123,7 +123,8 @@ specify a tag or wildcard (leave empty should also work) ## Configure datasource with provisioning -It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) + Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/influxdb.md b/docs/sources/features/datasources/influxdb.md index 6b0d92c8e6f..4c5109ef0bc 100644 --- a/docs/sources/features/datasources/influxdb.md +++ b/docs/sources/features/datasources/influxdb.md @@ -177,7 +177,8 @@ Tags field can be a comma separated string. ## Configure datasource with provisioning -It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) + Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/mysql.md b/docs/sources/features/datasources/mysql.md index 3b6daed622f..51483bfa36a 100644 --- a/docs/sources/features/datasources/mysql.md +++ b/docs/sources/features/datasources/mysql.md @@ -228,7 +228,8 @@ Time series queries should work in alerting conditions. Table formatted queries ## Configure datasource with provisioning -It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) + Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/opentsdb.md b/docs/sources/features/datasources/opentsdb.md index a233d4fefe8..d29753b0071 100644 --- a/docs/sources/features/datasources/opentsdb.md +++ b/docs/sources/features/datasources/opentsdb.md @@ -91,7 +91,8 @@ For details on OpenTSDB metric queries checkout the official [OpenTSDB documenta ## Configure datasource with provisioning -It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) + Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/postgres.md b/docs/sources/features/datasources/postgres.md index 6384d101dff..104aedc5d5d 100644 --- a/docs/sources/features/datasources/postgres.md +++ b/docs/sources/features/datasources/postgres.md @@ -220,7 +220,8 @@ conditions. ## Configure datasource with provisioning -It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources). +It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) + Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml diff --git a/docs/sources/features/datasources/prometheus.md b/docs/sources/features/datasources/prometheus.md index ba0ef626745..84cb8c2e333 100644 --- a/docs/sources/features/datasources/prometheus.md +++ b/docs/sources/features/datasources/prometheus.md @@ -103,7 +103,8 @@ Since 4.6.0 Grafana exposes metrics for Prometheus on the `/metrics` endpoint. W ## Configure datasource with provisioning -It's now possible to configure datasources using config files with Grafanas [provisioning system](/administration/provisioning/#datasources +It's now possible to configure datasources using config files with Grafanas provisioning system. You can read more about how it works and all the settings you can set for datasources on the [provisioning docs page](/administration/provisioning/#datasources) + Here are some examples of how you can configure the Cloudwatch datasource using configuration. ```yaml From 5f47523d7682c8297718a5cb18f2bd7a0b71387a Mon Sep 17 00:00:00 2001 From: Caleb Tote Date: Mon, 9 Apr 2018 15:09:37 -0400 Subject: [PATCH 27/49] Update annotations.md to contain correct annotations api path --- docs/sources/http_api/annotations.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/sources/http_api/annotations.md b/docs/sources/http_api/annotations.md index 19c2a5c386c..038ff085674 100644 --- a/docs/sources/http_api/annotations.md +++ b/docs/sources/http_api/annotations.md @@ -180,14 +180,14 @@ Content-Type: application/json ## Delete Annotation By Id -`DELETE /api/annotation/:id` +`DELETE /api/annotations/:id` Deletes the annotation that matches the specified id. **Example Request**: ```http -DELETE /api/annotation/1 HTTP/1.1 +DELETE /api/annotations/1 HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk @@ -204,14 +204,14 @@ Content-Type: application/json ## Delete Annotation By RegionId -`DELETE /api/annotation/region/:id` +`DELETE /api/annotations/region/:id` Deletes the annotation that matches the specified region id. A region is an annotation that covers a timerange and has a start and end time. In the Grafana database, this is a stored as two annotations connected by a region id. **Example Request**: ```http -DELETE /api/annotation/region/1 HTTP/1.1 +DELETE /api/annotations/region/1 HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk From c860f2105240282470258fb8bc401389ab4ac497 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Mon, 9 Apr 2018 21:33:18 +0200 Subject: [PATCH 28/49] playlist: add missing nginject attribute --- public/app/features/playlist/playlist_routes.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/public/app/features/playlist/playlist_routes.ts b/public/app/features/playlist/playlist_routes.ts index c94446c2c1b..b898820e371 100644 --- a/public/app/features/playlist/playlist_routes.ts +++ b/public/app/features/playlist/playlist_routes.ts @@ -1,5 +1,6 @@ import angular from 'angular'; +/** @ngInject */ function grafanaRoutes($routeProvider) { $routeProvider .when('/playlists', { From 6719bdf9bde8057716a606c5b68adfa0df4d11fc Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Tue, 10 Apr 2018 09:24:54 +0200 Subject: [PATCH 29/49] added @ngInject --- public/app/core/directives/metric_segment.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/app/core/directives/metric_segment.ts b/public/app/core/directives/metric_segment.ts index d1054b23793..3718d7fbd4a 100644 --- a/public/app/core/directives/metric_segment.ts +++ b/public/app/core/directives/metric_segment.ts @@ -2,6 +2,7 @@ import _ from 'lodash'; import $ from 'jquery'; import coreModule from '../core_module'; +/** @ngInject */ export function metricSegment($compile, $sce) { let inputTemplate = ' Date: Tue, 10 Apr 2018 22:22:12 +0200 Subject: [PATCH 30/49] alerting: handle invalid json format closes #11003 --- pkg/services/alerting/notifiers/dingding.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/services/alerting/notifiers/dingding.go b/pkg/services/alerting/notifiers/dingding.go index e32b9d34f91..14eacef5831 100644 --- a/pkg/services/alerting/notifiers/dingding.go +++ b/pkg/services/alerting/notifiers/dingding.go @@ -72,7 +72,10 @@ func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error { this.log.Error("Failed to create Json data", "error", err, "dingding", this.Name) } - body, _ := bodyJSON.MarshalJSON() + body, err := bodyJSON.MarshalJSON() + if err != nil { + return err + } cmd := &m.SendWebhookSync{ Url: this.Url, From 25ec7b5b027fbd99a7dd5c4f446d9a4f5e2c44ec Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 11 Apr 2018 14:30:39 +0200 Subject: [PATCH 31/49] scrollbar: use native scroll for page --- .../app/core/components/scroll/page_scroll.ts | 37 +++++++++++++++++++ public/app/core/core.ts | 2 + public/app/partials/dashboard.html | 4 +- public/sass/components/_panel_graph.scss | 1 + public/sass/components/_scrollbar.scss | 20 ++++------ public/views/index.template.html | 3 +- 6 files changed, 50 insertions(+), 17 deletions(-) create mode 100644 public/app/core/components/scroll/page_scroll.ts diff --git a/public/app/core/components/scroll/page_scroll.ts b/public/app/core/components/scroll/page_scroll.ts new file mode 100644 index 00000000000..4782ad6d060 --- /dev/null +++ b/public/app/core/components/scroll/page_scroll.ts @@ -0,0 +1,37 @@ +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; + }); + }, + }; +} + +coreModule.directive('pageScrollbar', pageScrollbar); diff --git a/public/app/core/core.ts b/public/app/core/core.ts index 353d8762a9a..fb7021fe883 100644 --- a/public/app/core/core.ts +++ b/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, diff --git a/public/app/partials/dashboard.html b/public/app/partials/dashboard.html index a3d5a4439ee..d07127b0115 100644 --- a/public/app/partials/dashboard.html +++ b/public/app/partials/dashboard.html @@ -1,8 +1,8 @@
-
-
+
+
diff --git a/public/sass/components/_panel_graph.scss b/public/sass/components/_panel_graph.scss index 031192dd984..a19e0072518 100644 --- a/public/sass/components/_panel_graph.scss +++ b/public/sass/components/_panel_graph.scss @@ -74,6 +74,7 @@ .graph-legend-scroll { position: relative; + overflow: auto !important; } .graph-legend-icon { diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index 4bd0807bb0b..d8d698e73a6 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -106,22 +106,16 @@ opacity: 0.9; } -// Srollbars +// Scrollbars // -// ::-webkit-scrollbar { -// width: 8px; -// height: 8px; -// } - -// ::-webkit-scrollbar:hover { -// height: 8px; -// } - ::-webkit-scrollbar { - // Hide system scrollbar (Mac OS X) - width: 0; - height: 0; + width: 8px; + height: 8px; +} + +::-webkit-scrollbar:hover { + height: 8px; } ::-webkit-scrollbar-button:start:decrement, diff --git a/public/views/index.template.html b/public/views/index.template.html index 4cb4a0d57ce..9f151527b88 100644 --- a/public/views/index.template.html +++ b/public/views/index.template.html @@ -40,8 +40,7 @@
- -
+