Bläddra i källkod

Merge remote-tracking branch 'grafana/master' into alpha-text2

* grafana/master:
  Explore: Enable click on name label
  Bumping grafana ui version (#15669)
  Need this to be available for plugins
  docs: 6.0 whats new
  Updated latest.json with 6.0
  Update CHANGELOG.md
  docs: grafana 6.0 has been released.
  moves social package to /login
  moves tracing packge into /infra
  Update CHANGELOG.md
  changelog: adds notes for #14509 and #15179
  graph: fixes click after scroll in series override menu
  moves metric package to /infra
  stackdriver: change reducer mapping for distribution metrics
  stackdriver: fix for float64 bounds for distribution metrics
  Fixed value dropdown not updating when it's current value updates, fixes #15566
ryan 6 år sedan
förälder
incheckning
d3cbc5638c
63 ändrade filer med 697 tillägg och 262 borttagningar
  1. 7 1
      CHANGELOG.md
  2. 3 5
      docs/sources/guides/whats-new-in-v6-0.md
  3. 2 2
      latest.json
  4. 1 1
      packages/grafana-ui/package.json
  5. 1 0
      packages/grafana-ui/src/utils/index.ts
  6. 1 1
      pkg/api/admin_users.go
  7. 1 1
      pkg/api/dashboard.go
  8. 1 1
      pkg/api/dashboard_snapshot.go
  9. 1 1
      pkg/api/dataproxy.go
  10. 1 1
      pkg/api/login.go
  11. 2 2
      pkg/api/login_oauth.go
  12. 1 1
      pkg/api/org.go
  13. 1 1
      pkg/api/org_invite.go
  14. 1 1
      pkg/api/search.go
  15. 1 1
      pkg/api/signup.go
  16. 1 1
      pkg/cmd/grafana-server/main.go
  17. 3 3
      pkg/cmd/grafana-server/server.go
  18. 0 0
      pkg/infra/metrics/graphitebridge/graphite.go
  19. 0 0
      pkg/infra/metrics/graphitebridge/graphite_test.go
  20. 0 0
      pkg/infra/metrics/metrics.go
  21. 1 1
      pkg/infra/metrics/service.go
  22. 1 1
      pkg/infra/metrics/settings.go
  23. 0 0
      pkg/infra/tracing/tracing.go
  24. 0 0
      pkg/infra/tracing/tracing_test.go
  25. 1 1
      pkg/infra/usagestats/service.go
  26. 1 1
      pkg/infra/usagestats/usage_stats.go
  27. 0 0
      pkg/login/social/common.go
  28. 0 0
      pkg/login/social/generic_oauth.go
  29. 0 0
      pkg/login/social/github_oauth.go
  30. 0 0
      pkg/login/social/gitlab_oauth.go
  31. 0 0
      pkg/login/social/google_oauth.go
  32. 0 0
      pkg/login/social/grafana_com_oauth.go
  33. 0 0
      pkg/login/social/social.go
  34. 1 1
      pkg/middleware/request_metrics.go
  35. 1 1
      pkg/services/alerting/eval_handler.go
  36. 1 1
      pkg/services/alerting/notifier.go
  37. 1 1
      pkg/services/alerting/reader.go
  38. 1 1
      pkg/services/alerting/result_handler.go
  39. 1 1
      pkg/services/sqlstore/dashboard.go
  40. 1 1
      pkg/services/sqlstore/datasource.go
  41. 1 1
      pkg/tsdb/cloudwatch/cloudwatch.go
  42. 1 1
      pkg/tsdb/cloudwatch/metric_find_query.go
  43. 3 1
      pkg/tsdb/stackdriver/stackdriver.go
  44. 49 2
      pkg/tsdb/stackdriver/stackdriver_test.go
  45. 0 0
      pkg/tsdb/stackdriver/test-data/3-series-response-distribution-exponential.json
  46. 209 0
      pkg/tsdb/stackdriver/test-data/4-series-response-distribution-explicit.json
  47. 1 1
      pkg/tsdb/stackdriver/types.go
  48. 7 1
      public/app/core/directives/dropdown_typeahead.ts
  49. 8 8
      public/app/core/directives/value_select_dropdown.ts
  50. 1 1
      public/app/features/dashboard/components/SubMenu/template.html
  51. 163 87
      public/app/plugins/datasource/influxdb/partials/query.editor.html
  52. 4 2
      public/app/plugins/datasource/mysql/partials/query.editor.html
  53. 4 2
      public/app/plugins/datasource/postgres/partials/query.editor.html
  54. 1 1
      public/app/plugins/datasource/prometheus/result_transformer.ts
  55. 3 2
      public/app/plugins/datasource/prometheus/specs/result_transformer.test.ts
  56. 2 1
      public/app/plugins/datasource/stackdriver/components/Aggregations.test.tsx
  57. 1 1
      public/app/plugins/datasource/stackdriver/components/Aggregations.tsx
  58. 1 1
      public/app/plugins/datasource/stackdriver/components/Metrics.tsx
  59. 1 1
      public/app/plugins/datasource/stackdriver/components/__snapshots__/Aggregations.test.tsx.snap
  60. 2 2
      public/app/plugins/datasource/stackdriver/components/__snapshots__/QueryEditor.test.tsx.snap
  61. 5 5
      public/app/plugins/datasource/stackdriver/constants.ts
  62. 1 1
      public/app/plugins/panel/graph/series_overrides_ctrl.ts
  63. 189 105
      public/app/plugins/panel/graph/tab_display.html

+ 7 - 1
CHANGELOG.md

@@ -1,4 +1,10 @@
-# Unreleased
+# 6.0.0 stable (2019-02-25)
+
+### Bug Fixes
+* **Stackdriver**: fix for float64 bounds for distribution metrics [#14509](https://github.com/grafana/grafana/issues/14509)
+* **Stackdriver**: no reducers available for distribution type [#15179](https://github.com/grafana/grafana/issues/15179)
+* **Dashboard**: fixes click after scroll in series override menu [#15621](https://github.com/grafana/grafana/issues/15621)
+* **MySQL**: fix mysql query using _interval_ms variable throws error [#14507](https://github.com/grafana/grafana/issues/14507)
 
 
 # 6.0.0-beta3 (2019-02-19)
 # 6.0.0-beta3 (2019-02-19)
 
 

+ 3 - 5
docs/sources/guides/whats-new-in-v6-0.md

@@ -14,8 +14,6 @@ weight = -11
 
 
 This update to Grafana introduces a new way of exploring your data, support for log data and tons of other features.
 This update to Grafana introduces a new way of exploring your data, support for log data and tons of other features.
 
 
-Grafana v6.0 is out in **Beta**, [Download Now!](https://grafana.com/grafana/download/beta)
-
 The main highlights are:
 The main highlights are:
 
 
 - [Explore]({{< relref "#explore" >}}) - A new query focused workflow for ad-hoc data exploration and troubleshooting.
 - [Explore]({{< relref "#explore" >}}) - A new query focused workflow for ad-hoc data exploration and troubleshooting.
@@ -107,9 +105,9 @@ continue to refine and start using in other panels.
 ### React Panels & Query Editors
 ### React Panels & Query Editors
 
 
 A major part of all the work that has gone into Grafana v6.0 has been on the migration to React. This investment
 A major part of all the work that has gone into Grafana v6.0 has been on the migration to React. This investment
-is part of the future proofing of Grafana and it's code base and ecosystem. Starting in v6.0 **Panels** and **Data
+is part of the future proofing of Grafana's code base and ecosystem. Starting in v6.0 **Panels** and **Data
 source** plugins can be written in React using our published `@grafana/ui` sdk library. More information on this
 source** plugins can be written in React using our published `@grafana/ui` sdk library. More information on this
-will be shared closer to or just after release.
+will be shared soon.
 
 
 {{< docs-imagebox img="/img/docs/v60/react_panels.png" max-width="600px" caption="React Panel" >}}
 {{< docs-imagebox img="/img/docs/v60/react_panels.png" max-width="600px" caption="React Panel" >}}
 <br />
 <br />
@@ -122,7 +120,7 @@ To get started read the guide: [Using Google Stackdriver in Grafana](/features/d
 
 
 ## Azure Monitor Datasource
 ## Azure Monitor Datasource
 
 
-One of the goals of the Grafana v6.0 release is to add support for the three major clouds. Amazon Cloudwatch has been a core datasource for years and Google Stackdriver is also now supported. We developed an external plugin for Azure Monitor last year and for this release the [plugin](https://grafana.com/plugins/grafana-azure-monitor-datasource) is being moved into Grafana to be one of the built-in datasources. For users of the external plugin, Grafana will automatically start using the built-in version. As a core datasource, the Azure Monitor datasource will get alerting support for the official 6.0 release.
+One of the goals of the Grafana v6.0 release is to add support for the three major clouds. Amazon Cloudwatch has been a core datasource for years and Google Stackdriver is also now supported. We developed an external plugin for Azure Monitor last year and for this release the [plugin](https://grafana.com/plugins/grafana-azure-monitor-datasource) is being moved into Grafana to be one of the built-in datasources. For users of the external plugin, Grafana will automatically start using the built-in version. As a core datasource, the Azure Monitor datasource is able to get alerting support, in the 6.0 release alerting is supported for the Azure Monitor service, with the rest to follow.
 
 
 The Azure Monitor datasource integrates four Azure services with Grafana - Azure Monitor, Azure Log Analytics, Azure Application Insights and Azure Application Insights Analytics.
 The Azure Monitor datasource integrates four Azure services with Grafana - Azure Monitor, Azure Log Analytics, Azure Application Insights and Azure Application Insights Analytics.
 
 

+ 2 - 2
latest.json

@@ -1,4 +1,4 @@
 {
 {
-  "stable": "5.4.3",
-  "testing": "5.4.3"
+  "stable": "6.0.0",
+  "testing": "6.0.0"
 }
 }

+ 1 - 1
packages/grafana-ui/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@grafana/ui",
   "name": "@grafana/ui",
-  "version": "6.0.0-alpha.0",
+  "version": "6.0.1-alpha.0",
   "description": "Grafana Components Library",
   "description": "Grafana Components Library",
   "keywords": [
   "keywords": [
     "typescript",
     "typescript",

+ 1 - 0
packages/grafana-ui/src/utils/index.ts

@@ -2,3 +2,4 @@ export * from './processTimeSeries';
 export * from './valueFormats/valueFormats';
 export * from './valueFormats/valueFormats';
 export * from './colors';
 export * from './colors';
 export * from './namedColorsPalette';
 export * from './namedColorsPalette';
+export { getMappedValue } from './valueMappings';

+ 1 - 1
pkg/api/admin_users.go

@@ -3,7 +3,7 @@ package api
 import (
 import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/metrics"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"
 )
 )

+ 1 - 1
pkg/api/dashboard.go

@@ -13,8 +13,8 @@ import (
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/components/dashdiffs"
 	"github.com/grafana/grafana/pkg/components/dashdiffs"
 	"github.com/grafana/grafana/pkg/components/simplejson"
 	"github.com/grafana/grafana/pkg/components/simplejson"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
-	"github.com/grafana/grafana/pkg/metrics"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/services/guardian"
 	"github.com/grafana/grafana/pkg/services/guardian"

+ 1 - 1
pkg/api/dashboard_snapshot.go

@@ -10,7 +10,7 @@ import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/components/simplejson"
 	"github.com/grafana/grafana/pkg/components/simplejson"
-	"github.com/grafana/grafana/pkg/metrics"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/guardian"
 	"github.com/grafana/grafana/pkg/services/guardian"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"

+ 1 - 1
pkg/api/dataproxy.go

@@ -2,7 +2,7 @@ package api
 
 
 import (
 import (
 	"github.com/grafana/grafana/pkg/api/pluginproxy"
 	"github.com/grafana/grafana/pkg/api/pluginproxy"
-	"github.com/grafana/grafana/pkg/metrics"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/plugins"
 )
 )

+ 1 - 1
pkg/api/login.go

@@ -7,9 +7,9 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/login"
 	"github.com/grafana/grafana/pkg/login"
-	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/middleware"
 	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"

+ 2 - 2
pkg/api/login_oauth.go

@@ -16,12 +16,12 @@ import (
 	"golang.org/x/oauth2"
 	"golang.org/x/oauth2"
 
 
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/login"
 	"github.com/grafana/grafana/pkg/login"
-	"github.com/grafana/grafana/pkg/metrics"
+	"github.com/grafana/grafana/pkg/login/social"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
-	"github.com/grafana/grafana/pkg/social"
 )
 )
 
 
 var (
 var (

+ 1 - 1
pkg/api/org.go

@@ -3,7 +3,7 @@ package api
 import (
 import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/metrics"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"

+ 1 - 1
pkg/api/org_invite.go

@@ -6,7 +6,7 @@ import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/events"
 	"github.com/grafana/grafana/pkg/events"
-	"github.com/grafana/grafana/pkg/metrics"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"

+ 1 - 1
pkg/api/search.go

@@ -4,7 +4,7 @@ import (
 	"strconv"
 	"strconv"
 
 
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/metrics"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/search"
 	"github.com/grafana/grafana/pkg/services/search"
 )
 )

+ 1 - 1
pkg/api/signup.go

@@ -4,7 +4,7 @@ import (
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/events"
 	"github.com/grafana/grafana/pkg/events"
-	"github.com/grafana/grafana/pkg/metrics"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"

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

@@ -14,8 +14,8 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/grafana/grafana/pkg/extensions"
 	"github.com/grafana/grafana/pkg/extensions"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
-	"github.com/grafana/grafana/pkg/metrics"
 	_ "github.com/grafana/grafana/pkg/services/alerting/conditions"
 	_ "github.com/grafana/grafana/pkg/services/alerting/conditions"
 	_ "github.com/grafana/grafana/pkg/services/alerting/notifiers"
 	_ "github.com/grafana/grafana/pkg/services/alerting/notifiers"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"

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

@@ -16,9 +16,9 @@ import (
 	"github.com/grafana/grafana/pkg/api/routing"
 	"github.com/grafana/grafana/pkg/api/routing"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/login"
 	"github.com/grafana/grafana/pkg/login"
+	"github.com/grafana/grafana/pkg/login/social"
 	"github.com/grafana/grafana/pkg/middleware"
 	"github.com/grafana/grafana/pkg/middleware"
 	"github.com/grafana/grafana/pkg/registry"
 	"github.com/grafana/grafana/pkg/registry"
-	"github.com/grafana/grafana/pkg/social"
 
 
 	"golang.org/x/sync/errgroup"
 	"golang.org/x/sync/errgroup"
 
 
@@ -28,8 +28,9 @@ import (
 
 
 	// self registering services
 	// self registering services
 	_ "github.com/grafana/grafana/pkg/extensions"
 	_ "github.com/grafana/grafana/pkg/extensions"
+	_ "github.com/grafana/grafana/pkg/infra/metrics"
 	_ "github.com/grafana/grafana/pkg/infra/serverlock"
 	_ "github.com/grafana/grafana/pkg/infra/serverlock"
-	_ "github.com/grafana/grafana/pkg/metrics"
+	_ "github.com/grafana/grafana/pkg/infra/tracing"
 	_ "github.com/grafana/grafana/pkg/plugins"
 	_ "github.com/grafana/grafana/pkg/plugins"
 	_ "github.com/grafana/grafana/pkg/services/alerting"
 	_ "github.com/grafana/grafana/pkg/services/alerting"
 	_ "github.com/grafana/grafana/pkg/services/auth"
 	_ "github.com/grafana/grafana/pkg/services/auth"
@@ -39,7 +40,6 @@ import (
 	_ "github.com/grafana/grafana/pkg/services/rendering"
 	_ "github.com/grafana/grafana/pkg/services/rendering"
 	_ "github.com/grafana/grafana/pkg/services/search"
 	_ "github.com/grafana/grafana/pkg/services/search"
 	_ "github.com/grafana/grafana/pkg/services/sqlstore"
 	_ "github.com/grafana/grafana/pkg/services/sqlstore"
-	_ "github.com/grafana/grafana/pkg/tracing"
 )
 )
 
 
 func NewGrafanaServer() *GrafanaServerImpl {
 func NewGrafanaServer() *GrafanaServerImpl {

+ 0 - 0
pkg/metrics/graphitebridge/graphite.go → pkg/infra/metrics/graphitebridge/graphite.go


+ 0 - 0
pkg/metrics/graphitebridge/graphite_test.go → pkg/infra/metrics/graphitebridge/graphite_test.go


+ 0 - 0
pkg/metrics/metrics.go → pkg/infra/metrics/metrics.go


+ 1 - 1
pkg/metrics/service.go → pkg/infra/metrics/service.go

@@ -3,8 +3,8 @@ package metrics
 import (
 import (
 	"context"
 	"context"
 
 
+	"github.com/grafana/grafana/pkg/infra/metrics/graphitebridge"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
-	"github.com/grafana/grafana/pkg/metrics/graphitebridge"
 	"github.com/grafana/grafana/pkg/registry"
 	"github.com/grafana/grafana/pkg/registry"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 )
 )

+ 1 - 1
pkg/metrics/settings.go → pkg/infra/metrics/settings.go

@@ -5,7 +5,7 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
-	"github.com/grafana/grafana/pkg/metrics/graphitebridge"
+	"github.com/grafana/grafana/pkg/infra/metrics/graphitebridge"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus"
 )
 )

+ 0 - 0
pkg/tracing/tracing.go → pkg/infra/tracing/tracing.go


+ 0 - 0
pkg/tracing/tracing_test.go → pkg/infra/tracing/tracing_test.go


+ 1 - 1
pkg/infra/usagestats/service.go

@@ -5,8 +5,8 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
+	"github.com/grafana/grafana/pkg/login/social"
 	"github.com/grafana/grafana/pkg/services/sqlstore"
 	"github.com/grafana/grafana/pkg/services/sqlstore"
-	"github.com/grafana/grafana/pkg/social"
 
 
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/registry"
 	"github.com/grafana/grafana/pkg/registry"

+ 1 - 1
pkg/infra/usagestats/usage_stats.go

@@ -9,7 +9,7 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
-	"github.com/grafana/grafana/pkg/metrics"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	"github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"

+ 0 - 0
pkg/social/common.go → pkg/login/social/common.go


+ 0 - 0
pkg/social/generic_oauth.go → pkg/login/social/generic_oauth.go


+ 0 - 0
pkg/social/github_oauth.go → pkg/login/social/github_oauth.go


+ 0 - 0
pkg/social/gitlab_oauth.go → pkg/login/social/gitlab_oauth.go


+ 0 - 0
pkg/social/google_oauth.go → pkg/login/social/google_oauth.go


+ 0 - 0
pkg/social/grafana_com_oauth.go → pkg/login/social/grafana_com_oauth.go


+ 0 - 0
pkg/social/social.go → pkg/login/social/social.go


+ 1 - 1
pkg/middleware/request_metrics.go

@@ -6,7 +6,7 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
-	"github.com/grafana/grafana/pkg/metrics"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	"gopkg.in/macaron.v1"
 	"gopkg.in/macaron.v1"
 )
 )
 
 

+ 1 - 1
pkg/services/alerting/eval_handler.go

@@ -5,8 +5,8 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
-	"github.com/grafana/grafana/pkg/metrics"
 )
 )
 
 
 type DefaultEvalHandler struct {
 type DefaultEvalHandler struct {

+ 1 - 1
pkg/services/alerting/notifier.go

@@ -7,8 +7,8 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/components/imguploader"
 	"github.com/grafana/grafana/pkg/components/imguploader"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
-	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/services/rendering"
 	"github.com/grafana/grafana/pkg/services/rendering"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/setting"
 
 

+ 1 - 1
pkg/services/alerting/reader.go

@@ -5,8 +5,8 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
-	"github.com/grafana/grafana/pkg/metrics"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 )
 )
 
 

+ 1 - 1
pkg/services/alerting/result_handler.go

@@ -5,8 +5,8 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/components/simplejson"
 	"github.com/grafana/grafana/pkg/components/simplejson"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
-	"github.com/grafana/grafana/pkg/metrics"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/annotations"
 	"github.com/grafana/grafana/pkg/services/annotations"
 	"github.com/grafana/grafana/pkg/services/rendering"
 	"github.com/grafana/grafana/pkg/services/rendering"

+ 1 - 1
pkg/services/sqlstore/dashboard.go

@@ -5,7 +5,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/metrics"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/search"
 	"github.com/grafana/grafana/pkg/services/search"
 	"github.com/grafana/grafana/pkg/util"
 	"github.com/grafana/grafana/pkg/util"

+ 1 - 1
pkg/services/sqlstore/datasource.go

@@ -7,7 +7,7 @@ import (
 
 
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/components/securejsondata"
 	"github.com/grafana/grafana/pkg/components/securejsondata"
-	"github.com/grafana/grafana/pkg/metrics"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 )
 )
 
 

+ 1 - 1
pkg/tsdb/cloudwatch/cloudwatch.go

@@ -24,7 +24,7 @@ import (
 	"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface"
 	"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface"
 	"github.com/grafana/grafana/pkg/components/null"
 	"github.com/grafana/grafana/pkg/components/null"
 	"github.com/grafana/grafana/pkg/components/simplejson"
 	"github.com/grafana/grafana/pkg/components/simplejson"
-	"github.com/grafana/grafana/pkg/metrics"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 )
 )
 
 
 type CloudWatchExecutor struct {
 type CloudWatchExecutor struct {

+ 1 - 1
pkg/tsdb/cloudwatch/metric_find_query.go

@@ -17,7 +17,7 @@ import (
 	"github.com/aws/aws-sdk-go/service/ec2"
 	"github.com/aws/aws-sdk-go/service/ec2"
 	"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
 	"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
 	"github.com/grafana/grafana/pkg/components/simplejson"
 	"github.com/grafana/grafana/pkg/components/simplejson"
-	"github.com/grafana/grafana/pkg/metrics"
+	"github.com/grafana/grafana/pkg/infra/metrics"
 	"github.com/grafana/grafana/pkg/tsdb"
 	"github.com/grafana/grafana/pkg/tsdb"
 )
 )
 
 

+ 3 - 1
pkg/tsdb/stackdriver/stackdriver.go

@@ -336,6 +336,8 @@ func (e *StackdriverExecutor) unmarshalResponse(res *http.Response) (Stackdriver
 		return StackdriverResponse{}, err
 		return StackdriverResponse{}, err
 	}
 	}
 
 
+	// slog.Info("stackdriver", "response", string(body))
+
 	if res.StatusCode/100 != 2 {
 	if res.StatusCode/100 != 2 {
 		slog.Error("Request failed", "status", res.Status, "body", string(body))
 		slog.Error("Request failed", "status", res.Status, "body", string(body))
 		return StackdriverResponse{}, fmt.Errorf(string(body))
 		return StackdriverResponse{}, fmt.Errorf(string(body))
@@ -559,7 +561,7 @@ func calcBucketBound(bucketOptions StackdriverBucketOptions, n int) string {
 	} else if bucketOptions.ExponentialBuckets != nil {
 	} else if bucketOptions.ExponentialBuckets != nil {
 		bucketBound = strconv.FormatInt(int64(bucketOptions.ExponentialBuckets.Scale*math.Pow(bucketOptions.ExponentialBuckets.GrowthFactor, float64(n-1))), 10)
 		bucketBound = strconv.FormatInt(int64(bucketOptions.ExponentialBuckets.Scale*math.Pow(bucketOptions.ExponentialBuckets.GrowthFactor, float64(n-1))), 10)
 	} else if bucketOptions.ExplicitBuckets != nil {
 	} else if bucketOptions.ExplicitBuckets != nil {
-		bucketBound = strconv.FormatInt(bucketOptions.ExplicitBuckets.Bounds[(n-1)], 10)
+		bucketBound = fmt.Sprintf("%g", bucketOptions.ExplicitBuckets.Bounds[n])
 	}
 	}
 	return bucketBound
 	return bucketBound
 }
 }

+ 49 - 2
pkg/tsdb/stackdriver/stackdriver_test.go

@@ -344,8 +344,8 @@ func TestStackdriver(t *testing.T) {
 				})
 				})
 			})
 			})
 
 
-			Convey("when data from query is distribution", func() {
-				data, err := loadTestFile("./test-data/3-series-response-distribution.json")
+			Convey("when data from query is distribution with exponential bounds", func() {
+				data, err := loadTestFile("./test-data/3-series-response-distribution-exponential.json")
 				So(err, ShouldBeNil)
 				So(err, ShouldBeNil)
 				So(len(data.TimeSeries), ShouldEqual, 1)
 				So(len(data.TimeSeries), ShouldEqual, 1)
 
 
@@ -370,6 +370,14 @@ func TestStackdriver(t *testing.T) {
 					So(res.Series[0].Points[2][1].Float64, ShouldEqual, 1536669060000)
 					So(res.Series[0].Points[2][1].Float64, ShouldEqual, 1536669060000)
 				})
 				})
 
 
+				Convey("bucket bounds should be correct", func() {
+					So(res.Series[0].Name, ShouldEqual, "0")
+					So(res.Series[1].Name, ShouldEqual, "1")
+					So(res.Series[2].Name, ShouldEqual, "2")
+					So(res.Series[3].Name, ShouldEqual, "4")
+					So(res.Series[4].Name, ShouldEqual, "8")
+				})
+
 				Convey("value should be correct", func() {
 				Convey("value should be correct", func() {
 					So(res.Series[8].Points[0][0].Float64, ShouldEqual, 1)
 					So(res.Series[8].Points[0][0].Float64, ShouldEqual, 1)
 					So(res.Series[9].Points[0][0].Float64, ShouldEqual, 1)
 					So(res.Series[9].Points[0][0].Float64, ShouldEqual, 1)
@@ -383,6 +391,45 @@ func TestStackdriver(t *testing.T) {
 				})
 				})
 			})
 			})
 
 
+			Convey("when data from query is distribution with explicit bounds", func() {
+				data, err := loadTestFile("./test-data/4-series-response-distribution-explicit.json")
+				So(err, ShouldBeNil)
+				So(len(data.TimeSeries), ShouldEqual, 1)
+
+				res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"}
+				query := &StackdriverQuery{AliasBy: "{{bucket}}"}
+				err = executor.parseResponse(res, data, query)
+				So(err, ShouldBeNil)
+
+				So(len(res.Series), ShouldEqual, 33)
+				for i := 0; i < 33; i++ {
+					if i == 0 {
+						So(res.Series[i].Name, ShouldEqual, "0")
+					}
+					So(len(res.Series[i].Points), ShouldEqual, 2)
+				}
+
+				Convey("timestamps should be in ascending order", func() {
+					So(res.Series[0].Points[0][1].Float64, ShouldEqual, 1550859086000)
+					So(res.Series[0].Points[1][1].Float64, ShouldEqual, 1550859146000)
+				})
+
+				Convey("bucket bounds should be correct", func() {
+					So(res.Series[0].Name, ShouldEqual, "0")
+					So(res.Series[1].Name, ShouldEqual, "0.01")
+					So(res.Series[2].Name, ShouldEqual, "0.05")
+					So(res.Series[3].Name, ShouldEqual, "0.1")
+				})
+
+				Convey("value should be correct", func() {
+					So(res.Series[8].Points[0][0].Float64, ShouldEqual, 381)
+					So(res.Series[9].Points[0][0].Float64, ShouldEqual, 212)
+					So(res.Series[10].Points[0][0].Float64, ShouldEqual, 56)
+					So(res.Series[8].Points[1][0].Float64, ShouldEqual, 375)
+					So(res.Series[9].Points[1][0].Float64, ShouldEqual, 213)
+					So(res.Series[10].Points[1][0].Float64, ShouldEqual, 56)
+				})
+			})
 		})
 		})
 
 
 		Convey("when interpolating filter wildcards", func() {
 		Convey("when interpolating filter wildcards", func() {

+ 0 - 0
pkg/tsdb/stackdriver/test-data/3-series-response-distribution.json → pkg/tsdb/stackdriver/test-data/3-series-response-distribution-exponential.json


+ 209 - 0
pkg/tsdb/stackdriver/test-data/4-series-response-distribution-explicit.json

@@ -0,0 +1,209 @@
+{
+  "timeSeries": [
+    {
+      "metric": {
+        "type": "custom.googleapis.com\/opencensus\/grpc.io\/client\/roundtrip_latency"
+      },
+      "resource": {
+        "type": "global",
+        "labels": {
+          "project_id": "grafana-demo"
+        }
+      },
+      "metricKind": "DELTA",
+      "valueType": "DISTRIBUTION",
+      "points": [
+        {
+          "interval": {
+            "startTime": "2019-02-22T18:11:26Z",
+            "endTime": "2019-02-22T18:12:26Z"
+          },
+          "value": {
+            "distributionValue": {
+              "count": "1878",
+              "mean": 17.813718392255,
+              "sumOfSquaredDeviation": 7141630.651914,
+              "bucketOptions": {
+                "explicitBuckets": {
+                  "bounds": [
+                    0,
+                    0.01,
+                    0.05,
+                    0.1,
+                    0.3,
+                    0.6,
+                    0.8,
+                    1,
+                    2,
+                    3,
+                    4,
+                    5,
+                    6,
+                    8,
+                    10,
+                    13,
+                    16,
+                    20,
+                    25,
+                    30,
+                    40,
+                    50,
+                    65,
+                    80,
+                    100,
+                    130,
+                    160,
+                    200,
+                    250,
+                    300,
+                    400,
+                    500,
+                    650,
+                    800,
+                    1000,
+                    2000,
+                    5000,
+                    10000,
+                    20000,
+                    50000,
+                    100000
+                  ]
+                }
+              },
+              "bucketCounts": [
+                "0",
+                "0",
+                "0",
+                "0",
+                "8",
+                "403",
+                "297",
+                "184",
+                "375",
+                "213",
+                "56",
+                "31",
+                "15",
+                "13",
+                "4",
+                "1",
+                "5",
+                "2",
+                "8",
+                "13",
+                "26",
+                "13",
+                "45",
+                "48",
+                "61",
+                "10",
+                "3",
+                "6",
+                "7",
+                "4",
+                "7",
+                "12",
+                "8"
+              ]
+            }
+          }
+        },
+        {
+          "interval": {
+            "startTime": "2019-02-22T18:10:26Z",
+            "endTime": "2019-02-22T18:11:26Z"
+          },
+          "value": {
+            "distributionValue": {
+              "count": "1887",
+              "mean": 17.654277577766,
+              "sumOfSquaredDeviation": 7082587.2133073,
+              "bucketOptions": {
+                "explicitBuckets": {
+                  "bounds": [
+                    0,
+                    0.01,
+                    0.05,
+                    0.1,
+                    0.3,
+                    0.6,
+                    0.8,
+                    1,
+                    2,
+                    3,
+                    4,
+                    5,
+                    6,
+                    8,
+                    10,
+                    13,
+                    16,
+                    20,
+                    25,
+                    30,
+                    40,
+                    50,
+                    65,
+                    80,
+                    100,
+                    130,
+                    160,
+                    200,
+                    250,
+                    300,
+                    400,
+                    500,
+                    650,
+                    800,
+                    1000,
+                    2000,
+                    5000,
+                    10000,
+                    20000,
+                    50000,
+                    100000
+                  ]
+                }
+              },
+              "bucketCounts": [
+                "0",
+                "0",
+                "0",
+                "0",
+                "8",
+                "404",
+                "298",
+                "187",
+                "381",
+                "212",
+                "56",
+                "31",
+                "15",
+                "14",
+                "4",
+                "1",
+                "4",
+                "2",
+                "9",
+                "13",
+                "24",
+                "13",
+                "46",
+                "46",
+                "61",
+                "11",
+                "3",
+                "6",
+                "7",
+                "5",
+                "7",
+                "11",
+                "8"
+              ]
+            }
+          }
+        }
+      ]
+    }
+  ]
+}

+ 1 - 1
pkg/tsdb/stackdriver/types.go

@@ -26,7 +26,7 @@ type StackdriverBucketOptions struct {
 		Scale            float64 `json:"scale"`
 		Scale            float64 `json:"scale"`
 	} `json:"exponentialBuckets"`
 	} `json:"exponentialBuckets"`
 	ExplicitBuckets *struct {
 	ExplicitBuckets *struct {
-		Bounds []int64 `json:"bounds"`
+		Bounds []float64 `json:"bounds"`
 	} `json:"explicitBuckets"`
 	} `json:"explicitBuckets"`
 }
 }
 
 

+ 7 - 1
public/app/core/directives/dropdown_typeahead.ts

@@ -128,7 +128,7 @@ export function dropdownTypeahead2($compile) {
     '<input type="text"' + ' class="gf-form-input"' + ' spellcheck="false" style="display:none"></input>';
     '<input type="text"' + ' class="gf-form-input"' + ' spellcheck="false" style="display:none"></input>';
 
 
   const buttonTemplate =
   const buttonTemplate =
-    '<a class="gf-form-input dropdown-toggle"' +
+    '<a class="{{buttonTemplateClass}} dropdown-toggle"' +
     ' tabindex="1" gf-dropdown="menuItems" data-toggle="dropdown"' +
     ' tabindex="1" gf-dropdown="menuItems" data-toggle="dropdown"' +
     ' ><i class="fa fa-plus"></i></a>';
     ' ><i class="fa fa-plus"></i></a>';
 
 
@@ -137,9 +137,15 @@ export function dropdownTypeahead2($compile) {
       menuItems: '=dropdownTypeahead2',
       menuItems: '=dropdownTypeahead2',
       dropdownTypeaheadOnSelect: '&dropdownTypeaheadOnSelect',
       dropdownTypeaheadOnSelect: '&dropdownTypeaheadOnSelect',
       model: '=ngModel',
       model: '=ngModel',
+      buttonTemplateClass: '@',
     },
     },
     link: ($scope, elem, attrs) => {
     link: ($scope, elem, attrs) => {
       const $input = $(inputTemplate);
       const $input = $(inputTemplate);
+
+      if (!$scope.buttonTemplateClass) {
+        $scope.buttonTemplateClass = 'gf-form-input';
+      }
+
       const $button = $(buttonTemplate);
       const $button = $(buttonTemplate);
       const timeoutId = {
       const timeoutId = {
         blur: null,
         blur: null,

+ 8 - 8
public/app/core/directives/value_select_dropdown.ts

@@ -240,7 +240,7 @@ export class ValueSelectDropdownCtrl {
 /** @ngInject */
 /** @ngInject */
 export function valueSelectDropdown($compile, $window, $timeout, $rootScope) {
 export function valueSelectDropdown($compile, $window, $timeout, $rootScope) {
   return {
   return {
-    scope: { variable: '=', onUpdated: '&' },
+    scope: { dashboard: '=', variable: '=', onUpdated: '&' },
     templateUrl: 'public/app/partials/valueSelectDropdown.html',
     templateUrl: 'public/app/partials/valueSelectDropdown.html',
     controller: 'ValueSelectDropdownCtrl',
     controller: 'ValueSelectDropdownCtrl',
     controllerAs: 'vm',
     controllerAs: 'vm',
@@ -288,13 +288,13 @@ export function valueSelectDropdown($compile, $window, $timeout, $rootScope) {
         }
         }
       });
       });
 
 
-      const cleanUp = $rootScope.$on('template-variable-value-updated', () => {
-        scope.vm.updateLinkText();
-      });
-
-      scope.$on('$destroy', () => {
-        cleanUp();
-      });
+      scope.vm.dashboard.on(
+        'template-variable-value-updated',
+        () => {
+          scope.vm.updateLinkText();
+        },
+        scope
+      );
 
 
       scope.vm.init();
       scope.vm.init();
     },
     },

+ 1 - 1
public/app/features/dashboard/components/SubMenu/template.html

@@ -4,7 +4,7 @@
       <label class="gf-form-label template-variable" ng-hide="variable.hide === 1">
       <label class="gf-form-label template-variable" ng-hide="variable.hide === 1">
         {{variable.label || variable.name}}
         {{variable.label || variable.name}}
       </label>
       </label>
-      <value-select-dropdown ng-if="variable.type !== 'adhoc' && variable.type !== 'textbox'" variable="variable" on-updated="ctrl.variableUpdated(variable)"></value-select-dropdown>
+      <value-select-dropdown ng-if="variable.type !== 'adhoc' && variable.type !== 'textbox'" dashboard="ctrl.dashboard" variable="variable" on-updated="ctrl.variableUpdated(variable)"></value-select-dropdown>
       <input type="text" ng-if="variable.type === 'textbox'" ng-model="variable.query" class="gf-form-input width-12"  ng-blur="variable.current.value != variable.query && variable.updateOptions() && ctrl.variableUpdated(variable);" ng-keydown="$event.keyCode === 13 && variable.current.value != variable.query && variable.updateOptions() && ctrl.variableUpdated(variable);" ></input>
       <input type="text" ng-if="variable.type === 'textbox'" ng-model="variable.query" class="gf-form-input width-12"  ng-blur="variable.current.value != variable.query && variable.updateOptions() && ctrl.variableUpdated(variable);" ng-keydown="$event.keyCode === 13 && variable.current.value != variable.query && variable.updateOptions() && ctrl.variableUpdated(variable);" ></input>
     </div>
     </div>
     <ad-hoc-filters ng-if="variable.type === 'adhoc'" variable="variable" dashboard="ctrl.dashboard"></ad-hoc-filters>
     <ad-hoc-filters ng-if="variable.type === 'adhoc'" variable="variable" dashboard="ctrl.dashboard"></ad-hoc-filters>

+ 163 - 87
public/app/plugins/datasource/influxdb/partials/query.editor.html

@@ -1,19 +1,38 @@
 <query-editor-row query-ctrl="ctrl" can-collapse="true" has-text-edit-mode="true">
 <query-editor-row query-ctrl="ctrl" can-collapse="true" has-text-edit-mode="true">
-
-	<div ng-if="ctrl.target.rawQuery">
+  <div ng-if="ctrl.target.rawQuery">
     <div class="gf-form">
     <div class="gf-form">
-      <textarea rows="3" class="gf-form-input" ng-model="ctrl.target.query" spellcheck="false" placeholder="InfluxDB Query" ng-model-onblur ng-change="ctrl.refresh()"></textarea>
+      <textarea
+        rows="3"
+        class="gf-form-input"
+        ng-model="ctrl.target.query"
+        spellcheck="false"
+        placeholder="InfluxDB Query"
+        ng-model-onblur
+        ng-change="ctrl.refresh()"
+      ></textarea>
     </div>
     </div>
     <div class="gf-form-inline">
     <div class="gf-form-inline">
       <div class="gf-form">
       <div class="gf-form">
         <label class="gf-form-label query-keyword">FORMAT AS</label>
         <label class="gf-form-label query-keyword">FORMAT AS</label>
         <div class="gf-form-select-wrapper">
         <div class="gf-form-select-wrapper">
-          <select class="gf-form-input gf-size-auto" ng-model="ctrl.target.resultFormat" ng-options="f.value as f.text for f in ctrl.resultFormats" ng-change="ctrl.refresh()"></select>
+          <select
+            class="gf-form-input gf-size-auto"
+            ng-model="ctrl.target.resultFormat"
+            ng-options="f.value as f.text for f in ctrl.resultFormats"
+            ng-change="ctrl.refresh()"
+          ></select>
         </div>
         </div>
       </div>
       </div>
-      <div class="gf-form max-width-25"  ng-hide="ctrl.target.resultFormat === 'table'">
+      <div class="gf-form max-width-25" ng-hide="ctrl.target.resultFormat === 'table'">
         <label class="gf-form-label query-keyword">ALIAS BY</label>
         <label class="gf-form-label query-keyword">ALIAS BY</label>
-        <input type="text" class="gf-form-input" ng-model="ctrl.target.alias" spellcheck='false' placeholder="Naming pattern" ng-blur="ctrl.refresh()">
+        <input
+          type="text"
+          class="gf-form-input"
+          ng-model="ctrl.target.alias"
+          spellcheck="false"
+          placeholder="Naming pattern"
+          ng-blur="ctrl.refresh()"
+        />
       </div>
       </div>
       <div class="gf-form gf-form--grow">
       <div class="gf-form gf-form--grow">
         <div class="gf-form-label gf-form-label--grow"></div>
         <div class="gf-form-label gf-form-label--grow"></div>
@@ -21,108 +40,154 @@
     </div>
     </div>
   </div>
   </div>
 
 
-	<div ng-if="!ctrl.target.rawQuery">
-
-		<div class="gf-form-inline">
-			<div class="gf-form">
-				<label class="gf-form-label query-keyword width-7">FROM</label>
-
-				<metric-segment segment="ctrl.policySegment" get-options="ctrl.getPolicySegments()" on-change="ctrl.policyChanged()"></metric-segment>
-				<metric-segment segment="ctrl.measurementSegment" get-options="ctrl.getMeasurements($query)" on-change="ctrl.measurementChanged()"></metric-segment>
-			</div>
-
-			<div class="gf-form">
-				<label class="gf-form-label query-keyword">WHERE</label>
-			</div>
-
-			<div class="gf-form" ng-repeat="segment in ctrl.tagSegments">
-				<metric-segment segment="segment" get-options="ctrl.getTagsOrValues(segment, $index)" on-change="ctrl.tagSegmentUpdated(segment, $index)"></metric-segment>
-			</div>
-
-			<div class="gf-form gf-form--grow">
-				<div class="gf-form-label gf-form-label--grow"></div>
-			</div>
-		</div>
-
-		<div class="gf-form-inline" ng-repeat="selectParts in ctrl.queryModel.selectModels">
-			<div class="gf-form">
-				<label class="gf-form-label query-keyword width-7">
-					<span ng-show="$index === 0">SELECT</span>&nbsp;
-				</label>
-			</div>
-
-			<div class="gf-form" ng-repeat="part in selectParts">
-				<query-part-editor class="gf-form-label query-part" part="part" handle-event="ctrl.handleSelectPartEvent(selectParts, part, $event)">
-				</query-part-editor>
-			</div>
-
-			<div class="gf-form">
-				<label class="dropdown"
-								dropdown-typeahead="ctrl.selectMenu"
-								dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)">
-				</label>
-			</div>
-
-			<div class="gf-form gf-form--grow">
-				<div class="gf-form-label gf-form-label--grow"></div>
-			</div>
-		</div>
-
-		<div class="gf-form-inline">
-			<div class="gf-form">
-				<label class="gf-form-label query-keyword width-7">
-					<span>GROUP BY</span>
-				</label>
-
-				<query-part-editor  ng-repeat="part in ctrl.queryModel.groupByParts"
-                            part="part" class="gf-form-label query-part"
-                            handle-event="ctrl.handleGroupByPartEvent(part, $index, $event)">
-				</query-part-editor>
-			</div>
-
-			<div class="gf-form">
-				<metric-segment segment="ctrl.groupBySegment" get-options="ctrl.getGroupByOptions()" on-change="ctrl.groupByAction(part, $index)"></metric-segment>
-			</div>
-
-			<div class="gf-form gf-form--grow">
-				<div class="gf-form-label gf-form-label--grow"></div>
-			</div>
-		</div>
+  <div ng-if="!ctrl.target.rawQuery">
+    <div class="gf-form-inline">
+      <div class="gf-form">
+        <label class="gf-form-label query-keyword width-7">FROM</label>
+
+        <metric-segment
+          segment="ctrl.policySegment"
+          get-options="ctrl.getPolicySegments()"
+          on-change="ctrl.policyChanged()"
+        ></metric-segment>
+        <metric-segment
+          segment="ctrl.measurementSegment"
+          get-options="ctrl.getMeasurements($query)"
+          on-change="ctrl.measurementChanged()"
+        ></metric-segment>
+      </div>
+
+      <div class="gf-form">
+        <label class="gf-form-label query-keyword">WHERE</label>
+      </div>
+
+      <div class="gf-form" ng-repeat="segment in ctrl.tagSegments">
+        <metric-segment
+          segment="segment"
+          get-options="ctrl.getTagsOrValues(segment, $index)"
+          on-change="ctrl.tagSegmentUpdated(segment, $index)"
+        ></metric-segment>
+      </div>
+
+      <div class="gf-form gf-form--grow">
+        <div class="gf-form-label gf-form-label--grow"></div>
+      </div>
+    </div>
+
+    <div class="gf-form-inline" ng-repeat="selectParts in ctrl.queryModel.selectModels">
+      <div class="gf-form">
+        <label class="gf-form-label query-keyword width-7"> <span ng-show="$index === 0">SELECT</span>&nbsp; </label>
+      </div>
+
+      <div class="gf-form" ng-repeat="part in selectParts">
+        <query-part-editor
+          class="gf-form-label query-part"
+          part="part"
+          handle-event="ctrl.handleSelectPartEvent(selectParts, part, $event)"
+        >
+        </query-part-editor>
+      </div>
+
+      <div class="gf-form">
+        <label
+          class="dropdown"
+          dropdown-typeahead2="ctrl.selectMenu"
+          dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)"
+          button-template-class="gf-form-label query-part"
+        >
+        </label>
+      </div>
+
+      <div class="gf-form gf-form--grow">
+        <div class="gf-form-label gf-form-label--grow"></div>
+      </div>
+    </div>
+
+    <div class="gf-form-inline">
+      <div class="gf-form">
+        <label class="gf-form-label query-keyword width-7">
+          <span>GROUP BY</span>
+        </label>
+
+        <query-part-editor
+          ng-repeat="part in ctrl.queryModel.groupByParts"
+          part="part"
+          class="gf-form-label query-part"
+          handle-event="ctrl.handleGroupByPartEvent(part, $index, $event)"
+        >
+        </query-part-editor>
+      </div>
+
+      <div class="gf-form">
+        <metric-segment
+          segment="ctrl.groupBySegment"
+          get-options="ctrl.getGroupByOptions()"
+          on-change="ctrl.groupByAction(part, $index)"
+        ></metric-segment>
+      </div>
+
+      <div class="gf-form gf-form--grow">
+        <div class="gf-form-label gf-form-label--grow"></div>
+      </div>
+    </div>
 
 
     <div class="gf-form-inline" ng-if="ctrl.target.orderByTime === 'DESC'">
     <div class="gf-form-inline" ng-if="ctrl.target.orderByTime === 'DESC'">
       <div class="gf-form">
       <div class="gf-form">
         <label class="gf-form-label query-keyword width-7">ORDER BY</label>
         <label class="gf-form-label query-keyword width-7">ORDER BY</label>
-        <label class="gf-form-label pointer" ng-click="ctrl.removeOrderByTime()">time <span class="query-keyword">DESC</span> <i class="fa fa-remove"></i></label>
+        <label class="gf-form-label pointer" ng-click="ctrl.removeOrderByTime()"
+          >time <span class="query-keyword">DESC</span> <i class="fa fa-remove"></i
+        ></label>
       </div>
       </div>
       <div class="gf-form gf-form--grow">
       <div class="gf-form gf-form--grow">
-				<div class="gf-form-label gf-form-label--grow"></div>
-			</div>
+        <div class="gf-form-label gf-form-label--grow"></div>
+      </div>
     </div>
     </div>
 
 
     <div class="gf-form-inline" ng-if="ctrl.target.limit">
     <div class="gf-form-inline" ng-if="ctrl.target.limit">
       <div class="gf-form">
       <div class="gf-form">
         <label class="gf-form-label query-keyword width-7">LIMIT</label>
         <label class="gf-form-label query-keyword width-7">LIMIT</label>
-        <input type="text" class="gf-form-input width-9" ng-model="ctrl.target.limit" spellcheck='false' placeholder="No Limit" ng-blur="ctrl.refresh()">
+        <input
+          type="text"
+          class="gf-form-input width-9"
+          ng-model="ctrl.target.limit"
+          spellcheck="false"
+          placeholder="No Limit"
+          ng-blur="ctrl.refresh()"
+        />
       </div>
       </div>
       <div class="gf-form gf-form--grow">
       <div class="gf-form gf-form--grow">
-				<div class="gf-form-label gf-form-label--grow"></div>
-			</div>
+        <div class="gf-form-label gf-form-label--grow"></div>
+      </div>
     </div>
     </div>
 
 
     <div class="gf-form-inline" ng-if="ctrl.target.slimit">
     <div class="gf-form-inline" ng-if="ctrl.target.slimit">
       <div class="gf-form">
       <div class="gf-form">
         <label class="gf-form-label query-keyword width-7">SLIMIT</label>
         <label class="gf-form-label query-keyword width-7">SLIMIT</label>
-        <input type="text" class="gf-form-input width-9" ng-model="ctrl.target.slimit" spellcheck='false' placeholder="No Limit" ng-blur="ctrl.refresh()">
+        <input
+          type="text"
+          class="gf-form-input width-9"
+          ng-model="ctrl.target.slimit"
+          spellcheck="false"
+          placeholder="No Limit"
+          ng-blur="ctrl.refresh()"
+        />
       </div>
       </div>
       <div class="gf-form gf-form--grow">
       <div class="gf-form gf-form--grow">
-				<div class="gf-form-label gf-form-label--grow"></div>
-			</div>
+        <div class="gf-form-label gf-form-label--grow"></div>
+      </div>
     </div>
     </div>
 
 
     <div class="gf-form-inline" ng-if="ctrl.target.tz">
     <div class="gf-form-inline" ng-if="ctrl.target.tz">
       <div class="gf-form">
       <div class="gf-form">
         <label class="gf-form-label query-keyword width-7">tz</label>
         <label class="gf-form-label query-keyword width-7">tz</label>
-        <input type="text" class="gf-form-input width-9" ng-model="ctrl.target.tz" spellcheck='false' placeholder="No Timezone" ng-blur="ctrl.refresh()">
+        <input
+          type="text"
+          class="gf-form-input width-9"
+          ng-model="ctrl.target.tz"
+          spellcheck="false"
+          placeholder="No Timezone"
+          ng-blur="ctrl.refresh()"
+        />
       </div>
       </div>
       <div class="gf-form gf-form--grow">
       <div class="gf-form gf-form--grow">
         <div class="gf-form-label gf-form-label--grow"></div>
         <div class="gf-form-label gf-form-label--grow"></div>
@@ -133,7 +198,12 @@
       <div class="gf-form">
       <div class="gf-form">
         <label class="gf-form-label query-keyword width-7">FORMAT AS</label>
         <label class="gf-form-label query-keyword width-7">FORMAT AS</label>
         <div class="gf-form-select-wrapper">
         <div class="gf-form-select-wrapper">
-          <select class="gf-form-input gf-size-auto" ng-model="ctrl.target.resultFormat" ng-options="f.value as f.text for f in ctrl.resultFormats" ng-change="ctrl.refresh()"></select>
+          <select
+            class="gf-form-input gf-size-auto"
+            ng-model="ctrl.target.resultFormat"
+            ng-options="f.value as f.text for f in ctrl.resultFormats"
+            ng-change="ctrl.refresh()"
+          ></select>
         </div>
         </div>
       </div>
       </div>
       <div class="gf-form gf-form--grow">
       <div class="gf-form gf-form--grow">
@@ -141,15 +211,21 @@
       </div>
       </div>
     </div>
     </div>
 
 
-    <div class="gf-form-inline"  ng-hide="ctrl.target.resultFormat === 'table'">
+    <div class="gf-form-inline" ng-hide="ctrl.target.resultFormat === 'table'">
       <div class="gf-form max-width-30">
       <div class="gf-form max-width-30">
         <label class="gf-form-label query-keyword width-7">ALIAS BY</label>
         <label class="gf-form-label query-keyword width-7">ALIAS BY</label>
-        <input type="text" class="gf-form-input" ng-model="ctrl.target.alias" spellcheck='false' placeholder="Naming pattern" ng-blur="ctrl.refresh()">
+        <input
+          type="text"
+          class="gf-form-input"
+          ng-model="ctrl.target.alias"
+          spellcheck="false"
+          placeholder="Naming pattern"
+          ng-blur="ctrl.refresh()"
+        />
       </div>
       </div>
       <div class="gf-form gf-form--grow">
       <div class="gf-form gf-form--grow">
         <div class="gf-form-label gf-form-label--grow"></div>
         <div class="gf-form-label gf-form-label--grow"></div>
       </div>
       </div>
     </div>
     </div>
   </div>
   </div>
-
 </query-editor-row>
 </query-editor-row>

+ 4 - 2
public/app/plugins/datasource/mysql/partials/query.editor.html

@@ -45,8 +45,10 @@
 
 
       <div class="gf-form">
       <div class="gf-form">
         <label class="dropdown"
         <label class="dropdown"
-                dropdown-typeahead="ctrl.selectMenu"
-                dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)">
+                dropdown-typeahead2="ctrl.selectMenu"
+                dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)"
+                button-template-class="gf-form-label query-part"
+                >
         </label>
         </label>
       </div>
       </div>
 
 

+ 4 - 2
public/app/plugins/datasource/postgres/partials/query.editor.html

@@ -45,8 +45,10 @@
 
 
       <div class="gf-form">
       <div class="gf-form">
         <label class="dropdown"
         <label class="dropdown"
-                dropdown-typeahead="ctrl.selectMenu"
-                dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)">
+                dropdown-typeahead2="ctrl.selectMenu"
+                dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)"
+                button-template-class="gf-form-label query-part"
+                >
         </label>
         </label>
       </div>
       </div>
 
 

+ 1 - 1
public/app/plugins/datasource/prometheus/result_transformer.ts

@@ -100,7 +100,7 @@ export class ResultTransformer {
     table.columns.push({ text: 'Time', type: 'time' });
     table.columns.push({ text: 'Time', type: 'time' });
     _.each(sortedLabels, (label, labelIndex) => {
     _.each(sortedLabels, (label, labelIndex) => {
       metricLabels[label] = labelIndex + 1;
       metricLabels[label] = labelIndex + 1;
-      table.columns.push({ text: label, filterable: !label.startsWith('__') });
+      table.columns.push({ text: label, filterable: true });
     });
     });
     const valueText = resultCount > 1 || valueWithRefId ? `Value #${refId}` : 'Value';
     const valueText = resultCount > 1 || valueWithRefId ? `Value #${refId}` : 'Value';
     table.columns.push({ text: valueText });
     table.columns.push({ text: valueText });

+ 3 - 2
public/app/plugins/datasource/prometheus/specs/result_transformer.test.ts

@@ -66,11 +66,12 @@ describe('Prometheus Result Transformer', () => {
       ]);
       ]);
       expect(table.columns).toMatchObject([
       expect(table.columns).toMatchObject([
         { text: 'Time', type: 'time' },
         { text: 'Time', type: 'time' },
-        { text: '__name__' },
-        { text: 'instance' },
+        { text: '__name__', filterable: true },
+        { text: 'instance', filterable: true },
         { text: 'job' },
         { text: 'job' },
         { text: 'Value' },
         { text: 'Value' },
       ]);
       ]);
+      expect(table.columns[4].filterable).toBeUndefined();
     });
     });
 
 
     it('should column title include refId if response count is more than 2', () => {
     it('should column title include refId if response count is more than 2', () => {

+ 2 - 1
public/app/plugins/datasource/stackdriver/components/Aggregations.test.tsx

@@ -49,7 +49,8 @@ describe('Aggregations', () => {
       });
       });
       it('', () => {
       it('', () => {
         const options = wrapper.state().aggOptions[0].options;
         const options = wrapper.state().aggOptions[0].options;
-        expect(options.length).toEqual(5);
+
+        expect(options.length).toEqual(10);
         expect(options.map(o => o.value)).toEqual(expect.arrayContaining(['REDUCE_NONE']));
         expect(options.map(o => o.value)).toEqual(expect.arrayContaining(['REDUCE_NONE']));
       });
       });
     });
     });

+ 1 - 1
public/app/plugins/datasource/stackdriver/components/Aggregations.tsx

@@ -73,7 +73,7 @@ export class Aggregations extends React.Component<Props, State> {
               value={crossSeriesReducer}
               value={crossSeriesReducer}
               variables={templateSrv.variables}
               variables={templateSrv.variables}
               options={aggOptions}
               options={aggOptions}
-              placeholder="Select Aggregation"
+              placeholder="Select Reducer"
               className="width-15"
               className="width-15"
             />
             />
           </div>
           </div>

+ 1 - 1
public/app/plugins/datasource/stackdriver/components/Metrics.tsx

@@ -185,7 +185,7 @@ export class Metrics extends React.Component<Props, State> {
                 },
                 },
               ]}
               ]}
               placeholder="Select Metric"
               placeholder="Select Metric"
-              className="width-15"
+              className="width-26"
             />
             />
           </div>
           </div>
           <div className="gf-form gf-form--grow">
           <div className="gf-form gf-form--grow">

+ 1 - 1
public/app/plugins/datasource/stackdriver/components/__snapshots__/Aggregations.test.tsx.snap

@@ -28,7 +28,7 @@ Array [
             <div
             <div
               className="css-0 gf-form-select-box__placeholder"
               className="css-0 gf-form-select-box__placeholder"
             >
             >
-              Select Aggregation
+              Select Reducer
             </div>
             </div>
             <div
             <div
               className="css-0"
               className="css-0"

+ 2 - 2
public/app/plugins/datasource/stackdriver/components/__snapshots__/QueryEditor.test.tsx.snap

@@ -72,7 +72,7 @@ Array [
         Metric
         Metric
       </span>
       </span>
       <div
       <div
-        className="css-0 gf-form-input gf-form-input--form-dropdown width-15"
+        className="css-0 gf-form-input gf-form-input--form-dropdown width-26"
         onKeyDown={[Function]}
         onKeyDown={[Function]}
       >
       >
         <div
         <div
@@ -196,7 +196,7 @@ Array [
             <div
             <div
               className="css-0 gf-form-select-box__placeholder"
               className="css-0 gf-form-select-box__placeholder"
             >
             >
-              Select Aggregation
+              Select Reducer
             </div>
             </div>
             <div
             <div
               className="css-0"
               className="css-0"

+ 5 - 5
public/app/plugins/datasource/stackdriver/constants.ts

@@ -189,7 +189,7 @@ export const aggOptions = [
       ValueTypes.BOOL,
       ValueTypes.BOOL,
       ValueTypes.STRING,
       ValueTypes.STRING,
     ],
     ],
-    metricKinds: [MetricKind.GAUGE, MetricKind.DELTA],
+    metricKinds: [MetricKind.GAUGE, MetricKind.DELTA, MetricKind.CUMULATIVE],
   },
   },
   {
   {
     text: 'count true',
     text: 'count true',
@@ -207,25 +207,25 @@ export const aggOptions = [
     text: '99th percentile',
     text: '99th percentile',
     value: 'REDUCE_PERCENTILE_99',
     value: 'REDUCE_PERCENTILE_99',
     valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION],
     valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION],
-    metricKinds: [MetricKind.GAUGE, MetricKind.DELTA],
+    metricKinds: [MetricKind.GAUGE, MetricKind.DELTA, MetricKind.CUMULATIVE],
   },
   },
   {
   {
     text: '95th percentile',
     text: '95th percentile',
     value: 'REDUCE_PERCENTILE_95',
     value: 'REDUCE_PERCENTILE_95',
     valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION],
     valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION],
-    metricKinds: [MetricKind.GAUGE, MetricKind.DELTA],
+    metricKinds: [MetricKind.GAUGE, MetricKind.DELTA, MetricKind.CUMULATIVE],
   },
   },
   {
   {
     text: '50th percentile',
     text: '50th percentile',
     value: 'REDUCE_PERCENTILE_50',
     value: 'REDUCE_PERCENTILE_50',
     valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION],
     valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION],
-    metricKinds: [MetricKind.GAUGE, MetricKind.DELTA],
+    metricKinds: [MetricKind.GAUGE, MetricKind.DELTA, MetricKind.CUMULATIVE],
   },
   },
   {
   {
     text: '5th percentile',
     text: '5th percentile',
     value: 'REDUCE_PERCENTILE_05',
     value: 'REDUCE_PERCENTILE_05',
     valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION],
     valueTypes: [ValueTypes.INT64, ValueTypes.DOUBLE, ValueTypes.MONEY, ValueTypes.DISTRIBUTION],
-    metricKinds: [MetricKind.GAUGE, MetricKind.DELTA],
+    metricKinds: [MetricKind.GAUGE, MetricKind.DELTA, MetricKind.CUMULATIVE],
   },
   },
 ];
 ];
 
 

+ 1 - 1
public/app/plugins/panel/graph/series_overrides_ctrl.ts

@@ -11,7 +11,7 @@ export function SeriesOverridesCtrl($scope, $element, popoverSrv) {
     const option = {
     const option = {
       text: name,
       text: name,
       propertyName: propertyName,
       propertyName: propertyName,
-      index: $scope.overrideMenu.lenght,
+      index: $scope.overrideMenu.length,
       values: values,
       values: values,
       submenu: _.map(values, value => {
       submenu: _.map(values, value => {
         return { text: String(value), value: value };
         return { text: String(value), value: value };

+ 189 - 105
public/app/plugins/panel/graph/tab_display.html

@@ -1,110 +1,194 @@
+<div class="editor-row">
+  <div class="section gf-form-group">
+    <h5 class="section-heading">Draw Modes</h5>
+    <gf-form-switch
+      class="gf-form"
+      label="Bars"
+      label-class="width-5"
+      checked="ctrl.panel.bars"
+      on-change="ctrl.render()"
+    ></gf-form-switch>
+    <gf-form-switch
+      class="gf-form"
+      label="Lines"
+      label-class="width-5"
+      checked="ctrl.panel.lines"
+      on-change="ctrl.render()"
+    ></gf-form-switch>
+    <gf-form-switch
+      class="gf-form"
+      label="Points"
+      label-class="width-5"
+      checked="ctrl.panel.points"
+      on-change="ctrl.render()"
+    ></gf-form-switch>
+  </div>
+  <div class="section gf-form-group">
+    <h5 class="section-heading">Mode Options</h5>
+    <div class="gf-form">
+      <label class="gf-form-label width-8">Fill</label>
+      <div class="gf-form-select-wrapper max-width-5">
+        <select
+          class="gf-form-input"
+          ng-model="ctrl.panel.fill"
+          ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]"
+          ng-change="ctrl.render()"
+          ng-disabled="!ctrl.panel.lines"
+        ></select>
+      </div>
+    </div>
+    <div class="gf-form">
+      <label class="gf-form-label width-8">Line Width</label>
+      <div class="gf-form-select-wrapper max-width-5">
+        <select
+          class="gf-form-input"
+          ng-model="ctrl.panel.linewidth"
+          ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]"
+          ng-change="ctrl.render()"
+          ng-disabled="!ctrl.panel.lines"
+        ></select>
+      </div>
+    </div>
+    <gf-form-switch
+      ng-disabled="!ctrl.panel.lines"
+      class="gf-form"
+      label="Staircase"
+      label-class="width-8"
+      checked="ctrl.panel.steppedLine"
+      on-change="ctrl.render()"
+    >
+    </gf-form-switch>
+    <div class="gf-form" ng-if="ctrl.panel.points">
+      <label class="gf-form-label width-8">Point Radius</label>
+      <div class="gf-form-select-wrapper max-width-5">
+        <select
+          class="gf-form-input"
+          ng-model="ctrl.panel.pointradius"
+          ng-options="f for f in [0.5,1,2,3,4,5,6,7,8,9,10]"
+          ng-change="ctrl.render()"
+        ></select>
+      </div>
+    </div>
+  </div>
+  <div class="section gf-form-group">
+    <h5 class="section-heading">Hover tooltip</h5>
+    <div class="gf-form">
+      <label class="gf-form-label width-9">Mode</label>
+      <div class="gf-form-select-wrapper max-width-8">
+        <select
+          class="gf-form-input"
+          ng-model="ctrl.panel.tooltip.shared"
+          ng-options="f.value as f.text for f in [{text: 'All series', value: true}, {text: 'Single', value: false}]"
+          ng-change="ctrl.render()"
+        ></select>
+      </div>
+    </div>
+    <div class="gf-form">
+      <label class="gf-form-label width-9">Sort order</label>
+      <div class="gf-form-select-wrapper max-width-8">
+        <select
+          class="gf-form-input"
+          ng-model="ctrl.panel.tooltip.sort"
+          ng-options="f.value as f.text for f in [{text: 'None', value: 0}, {text: 'Increasing', value: 1}, {text: 'Decreasing', value: 2}]"
+          ng-change="ctrl.render()"
+        ></select>
+      </div>
+    </div>
+    <div class="gf-form" ng-show="ctrl.panel.stack">
+      <label class="gf-form-label width-9">Stacked value</label>
+      <div class="gf-form-select-wrapper max-width-8">
+        <select
+          class="gf-form-input"
+          ng-model="ctrl.panel.tooltip.value_type"
+          ng-options="f for f in ['cumulative','individual']"
+          ng-change="ctrl.render()"
+        ></select>
+      </div>
+    </div>
+  </div>
 
 
-	<div class="editor-row">
-		<div class="section gf-form-group">
-			<h5 class="section-heading">Draw Modes</h5>
-			<gf-form-switch class="gf-form" label="Bars" label-class="width-5" checked="ctrl.panel.bars" on-change="ctrl.render()"></gf-form-switch>
-			<gf-form-switch class="gf-form" label="Lines" label-class="width-5" checked="ctrl.panel.lines" on-change="ctrl.render()"></gf-form-switch>
-			<gf-form-switch class="gf-form" label="Points" label-class="width-5" checked="ctrl.panel.points" on-change="ctrl.render()"></gf-form-switch>
-		</div>
-		<div class="section gf-form-group">
-			<h5 class="section-heading">Mode Options</h5>
-			<div class="gf-form">
-				<label class="gf-form-label width-8">Fill</label>
-				<div class="gf-form-select-wrapper max-width-5">
-					<select class="gf-form-input" ng-model="ctrl.panel.fill" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]" ng-change="ctrl.render()" ng-disabled="!ctrl.panel.lines"></select>
-				</div>
-			</div>
-			<div class="gf-form">
-				<label class="gf-form-label width-8">Line Width</label>
-				<div class="gf-form-select-wrapper max-width-5">
-					<select class="gf-form-input" ng-model="ctrl.panel.linewidth" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10]" ng-change="ctrl.render()" ng-disabled="!ctrl.panel.lines"></select>
-				</div>
-			</div>
-			<gf-form-switch ng-disabled="!ctrl.panel.lines" class="gf-form" label="Staircase" label-class="width-8" checked="ctrl.panel.steppedLine" on-change="ctrl.render()">
-			</gf-form-switch>
-			<div class="gf-form" ng-if="ctrl.panel.points">
-				<label class="gf-form-label width-8">Point Radius</label>
-				<div class="gf-form-select-wrapper max-width-5">
-					<select class="gf-form-input" ng-model="ctrl.panel.pointradius" ng-options="f for f in [0.5,1,2,3,4,5,6,7,8,9,10]" ng-change="ctrl.render()"></select>
-				</div>
-			</div>
-		</div>
-		<div class="section gf-form-group">
-			<h5 class="section-heading">Hover tooltip</h5>
-			<div class="gf-form">
-				<label class="gf-form-label width-9">Mode</label>
-				<div class="gf-form-select-wrapper max-width-8">
-					<select class="gf-form-input" ng-model="ctrl.panel.tooltip.shared" ng-options="f.value as f.text for f in [{text: 'All series', value: true}, {text: 'Single', value: false}]" ng-change="ctrl.render()"></select>
-				</div>
-			</div>
-			<div class="gf-form">
-				<label class="gf-form-label width-9">Sort order</label>
-				<div class="gf-form-select-wrapper max-width-8">
-					<select class="gf-form-input" ng-model="ctrl.panel.tooltip.sort" ng-options="f.value as f.text for f in [{text: 'None', value: 0}, {text: 'Increasing', value: 1}, {text: 'Decreasing', value: 2}]" ng-change="ctrl.render()"></select>
-				</div>
-			</div>
-			<div class="gf-form" ng-show="ctrl.panel.stack">
-				<label class="gf-form-label width-9">Stacked value</label>
-				<div class="gf-form-select-wrapper max-width-8">
-					<select class="gf-form-input" ng-model="ctrl.panel.tooltip.value_type" ng-options="f for f in ['cumulative','individual']" ng-change="ctrl.render()"></select>
-				</div>
-			</div>
-		</div>
+  <div class="section gf-form-group">
+    <h5 class="section-heading">Stacking & Null value</h5>
+    <gf-form-switch
+      class="gf-form"
+      label="Stack"
+      label-class="width-7"
+      checked="ctrl.panel.stack"
+      on-change="ctrl.refresh()"
+    >
+    </gf-form-switch>
+    <gf-form-switch
+      class="gf-form"
+      ng-show="ctrl.panel.stack"
+      label="Percent"
+      label-class="width-7"
+      checked="ctrl.panel.percentage"
+      on-change="ctrl.render()"
+    >
+    </gf-form-switch>
+    <div class="gf-form">
+      <label class="gf-form-label width-7">Null value</label>
+      <div class="gf-form-select-wrapper">
+        <select
+          class="gf-form-input max-width-9"
+          ng-model="ctrl.panel.nullPointMode"
+          ng-options="f for f in ['connected', 'null', 'null as zero']"
+          ng-change="ctrl.render()"
+        ></select>
+      </div>
+    </div>
+  </div>
+</div>
 
 
-		<div class="section gf-form-group">
-			<h5 class="section-heading">Stacking & Null value</h5>
-			<gf-form-switch class="gf-form" label="Stack" label-class="width-7" checked="ctrl.panel.stack" on-change="ctrl.refresh()">
-			</gf-form-switch>
-			<gf-form-switch class="gf-form" ng-show="ctrl.panel.stack" label="Percent" label-class="width-7" checked="ctrl.panel.percentage" on-change="ctrl.render()">
-			</gf-form-switch>
-			<div class="gf-form">
-				<label class="gf-form-label width-7">Null value</label>
-				<div class="gf-form-select-wrapper">
-					<select class="gf-form-input max-width-9" ng-model="ctrl.panel.nullPointMode" ng-options="f for f in ['connected', 'null', 'null as zero']" ng-change="ctrl.render()"></select>
-				</div>
-			</div>
-		</div>
-	</div>
+<div>
+  <div class="gf-form-inline" ng-repeat="override in ctrl.panel.seriesOverrides" ng-controller="SeriesOverridesCtrl">
+    <div class="gf-form">
+      <label class="gf-form-label">alias or regex</label>
+    </div>
+    <div class="gf-form width-15">
+      <input
+        type="text"
+        ng-model="override.alias"
+        bs-typeahead="getSeriesNames"
+        ng-blur="ctrl.render()"
+        data-min-length="0"
+        data-items="100"
+        class="gf-form-input width-15"
+      />
+    </div>
+    <div class="gf-form" ng-repeat="option in currentOverrides">
+      <label class="gf-form-label">
+        <i class="pointer fa fa-remove" ng-click="removeOverride(option)"></i>
+        <span ng-show="option.propertyName === 'color'">
+          Color: <i class="fa fa-circle" ng-style="{color:option.value}"></i>
+        </span>
+        <span ng-show="option.propertyName !== 'color'"> {{ option.name }}: {{ option.value }} </span>
+      </label>
+    </div>
 
 
-		<div>
-			<div class="gf-form-inline" ng-repeat="override in ctrl.panel.seriesOverrides" ng-controller="SeriesOverridesCtrl">
-				<div class="gf-form">
-					<label class="gf-form-label">alias or regex</label>
-				</div>
-				<div class="gf-form width-15">
-					<input type="text" ng-model="override.alias" bs-typeahead="getSeriesNames" ng-blur="ctrl.render()" data-min-length=0 data-items=100 class="gf-form-input width-15">
-				</div>
-				<div class="gf-form" ng-repeat="option in currentOverrides">
-					<label class="gf-form-label">
-						<i class="pointer fa fa-remove" ng-click="removeOverride(option)"></i>
-						<span ng-show="option.propertyName === 'color'">
-							Color: <i class="fa fa-circle" ng-style="{color:option.value}"></i>
-						</span>
-						<span ng-show="option.propertyName !== 'color'">
-							{{option.name}}: {{option.value}}
-						</span>
-					</label>
-				</div>
+    <div class="gf-form">
+      <span
+        class="dropdown"
+        dropdown-typeahead2="overrideMenu"
+        dropdown-typeahead-on-select="setOverride($item, $subItem)"
+        button-template-class="gf-form-label"
+      >
+      </span>
+    </div>
 
 
-				<div class="gf-form">
-					<span class="dropdown" dropdown-typeahead="overrideMenu" dropdown-typeahead-on-select="setOverride($item, $subItem)">
-					</span>
-				</div>
-
-				<div class="gf-form gf-form--grow">
-					<div class="gf-form-label gf-form-label--grow"></div>
-				</div>
-
-				<div class="gf-form">
-					<label class="gf-form-label">
-						<i class="fa fa-trash pointer" ng-click="ctrl.removeSeriesOverride(override)"></i>
-					</label>
-				</div>
-			</div>
-			<div class="gf-form-button-row">
-				<button class="btn btn-inverse" ng-click="ctrl.addSeriesOverride()">
-					<i class="fa fa-plus"></i>&nbsp;Add series override<tip>Regex match example: /server[0-3]/i </tip>
-				</button>
-			</div>
-		</div>
+    <div class="gf-form gf-form--grow">
+      <div class="gf-form-label gf-form-label--grow"></div>
+    </div>
 
 
+    <div class="gf-form">
+      <label class="gf-form-label">
+        <i class="fa fa-trash pointer" ng-click="ctrl.removeSeriesOverride(override)"></i>
+      </label>
+    </div>
+  </div>
+  <div class="gf-form-button-row">
+    <button class="btn btn-inverse" ng-click="ctrl.addSeriesOverride()">
+      <i class="fa fa-plus"></i>&nbsp;Add series override<tip>Regex match example: /server[0-3]/i </tip>
+    </button>
+  </div>
+</div>