From 9b60a6377881d7da826baef9daa628ec152dcb7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 30 Aug 2017 16:39:00 +0200 Subject: [PATCH 1/6] ux: metrics tab v3 --- .../components/code_editor/code_editor.ts | 1 - public/app/features/panel/metrics_tab.ts | 5 ++ .../features/panel/partials/metrics_tab.html | 63 ++++++++++++------- .../features/panel/query_troubleshooter.ts | 23 ++++--- public/sass/components/_gf-form.scss | 4 -- public/sass/components/_query_editor.scss | 22 +++++++ 6 files changed, 84 insertions(+), 34 deletions(-) diff --git a/public/app/core/components/code_editor/code_editor.ts b/public/app/core/components/code_editor/code_editor.ts index d2d32d1f46e..151ebffab5d 100644 --- a/public/app/core/components/code_editor/code_editor.ts +++ b/public/app/core/components/code_editor/code_editor.ts @@ -159,7 +159,6 @@ function link(scope, elem, attrs) { enableSnippets: true }); - console.log('getting completer', lang); if (scope.getCompleter()) { // make copy of array as ace seems to share completers array between instances codeEditor.completers = codeEditor.completers.slice(); diff --git a/public/app/features/panel/metrics_tab.ts b/public/app/features/panel/metrics_tab.ts index e1825173172..82fa3be99e7 100644 --- a/public/app/features/panel/metrics_tab.ts +++ b/public/app/features/panel/metrics_tab.ts @@ -13,6 +13,7 @@ export class MetricsTabCtrl { dashboard: DashboardModel; panelDsValue: any; addQueryDropdown: any; + queryTroubleshooterOpen: boolean; /** @ngInject */ constructor($scope, private uiSegmentSrv, private datasourceSrv) { @@ -65,6 +66,10 @@ export class MetricsTabCtrl { addQuery() { this.panelCtrl.addQuery({isNew: true}); } + + toggleQueryTroubleshooter() { + this.queryTroubleshooterOpen = !this.queryTroubleshooterOpen; + } } /** @ngInject **/ diff --git a/public/app/features/panel/partials/metrics_tab.html b/public/app/features/panel/partials/metrics_tab.html index bc0bcf7c6b2..8ceee90e7ff 100644 --- a/public/app/features/panel/partials/metrics_tab.html +++ b/public/app/features/panel/partials/metrics_tab.html @@ -1,3 +1,45 @@ +
+
+
+ + + +
+
+ + + + A lower limit for the auto group by time interval. Recommended to be set to write frequency, + for example 1m if your data is written every minute. Access auto interval via variable $__interval for time range + string and $__interval_ms for numeric variable that can be used in math expressions. + +
+
+ +
+
+ +
+
+ +
+
+ +
+
@@ -27,24 +69,3 @@
- - - -
-
-
- - - -
-
-
- - - - - - diff --git a/public/app/features/panel/query_troubleshooter.ts b/public/app/features/panel/query_troubleshooter.ts index 84ae2045dc7..94357e53e5d 100644 --- a/public/app/features/panel/query_troubleshooter.ts +++ b/public/app/features/panel/query_troubleshooter.ts @@ -5,9 +5,8 @@ import appEvents from 'app/core/app_events'; import {coreModule, JsonExplorer} from 'app/core/core'; const template = ` - - +
+
Expand All @@ -15,12 +14,12 @@ const template = ` Collapse All Copy to Clipboard - - +
+
- - +
+
`; export class QueryTroubleshooterCtrl { @@ -42,7 +41,9 @@ export class QueryTroubleshooterCtrl { appEvents.on('ds-request-response', this.onRequestResponseEventListener); appEvents.on('ds-request-error', this.onRequestErrorEventListener); + $scope.$on('$destroy', this.removeEventsListeners.bind(this)); + $scope.$watch('ctrl.isOpen', this.stateChanged.bind(this)); } removeEventsListeners() { @@ -51,6 +52,11 @@ export class QueryTroubleshooterCtrl { } onRequestError(err) { + // ignore if closed + if (!this.isOpen) { + return; + } + this.isOpen = true; this.hasError = true; this.onRequestResponse(err); @@ -133,7 +139,8 @@ export function queryTroubleshooter() { bindToController: true, controllerAs: 'ctrl', scope: { - panelCtrl: "=" + panelCtrl: "=", + isOpen: "=", }, link: function(scope, elem, attrs, ctrl) { diff --git a/public/sass/components/_gf-form.scss b/public/sass/components/_gf-form.scss index e70b88c96cd..a330cc779a0 100644 --- a/public/sass/components/_gf-form.scss +++ b/public/sass/components/_gf-form.scss @@ -54,7 +54,6 @@ $gf-form-margin: 0.25rem; background-color: $input-label-bg; display: block; font-size: $font-size-sm; - margin-right: $gf-form-margin; border: $input-btn-border-width solid transparent; @include border-radius($label-border-radius-sm); @@ -103,7 +102,6 @@ $gf-form-margin: 0.25rem; padding: $input-padding-y $input-padding-x; margin-right: $gf-form-margin; font-size: $font-size-base; - margin-right: $gf-form-margin; line-height: $input-line-height; color: $input-color; background-color: $input-bg; @@ -112,7 +110,6 @@ $gf-form-margin: 0.25rem; border: $input-btn-border-width solid $input-border-color; @include border-radius($input-border-radius-sm); @include box-shadow($input-box-shadow); - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -235,7 +232,6 @@ $gf-form-margin: 0.25rem; font-size: $font-size-sm; box-shadow: none; border: $input-btn-border-width solid transparent; - @include border-radius($label-border-radius-sm); flex-shrink: 0; flex-grow: 0; diff --git a/public/sass/components/_query_editor.scss b/public/sass/components/_query_editor.scss index f2d29572e37..737abb593de 100644 --- a/public/sass/components/_query_editor.scss +++ b/public/sass/components/_query_editor.scss @@ -146,3 +146,25 @@ input[type="text"].tight-form-func-param { margin-left: 10px; } } + +.query-troubleshooter { + font-size: $font-size-sm; + margin: $gf-form-margin; + border: 1px solid $btn-secondary-bg; + min-height: 100px; + border-radius: 3px; +} + +.query-troubleshooter__header { + float: right; + font-size: $font-size-sm; + text-align: right; + padding: $input-padding-y $input-padding-x; + a { + margin-left: $spacer; + } +} + +.query-troubleshooter__body { + padding: $spacer 0; +} From 84d4958a3cde902f5a5a9d5af16e728a27e4b218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 31 Aug 2017 14:05:52 +0200 Subject: [PATCH 2/6] plugin change: make interval, cache timeout & max data points options in plugin.json, remove query.options component feature, add help markdown feature and toggle for data sources --- pkg/api/api.go | 2 +- pkg/api/plugins.go | 7 +- pkg/plugins/datasource_plugin.go | 32 ++++-- pkg/plugins/models.go | 7 +- pkg/plugins/plugins.go | 23 ++-- public/app/features/panel/metrics_tab.ts | 32 +++++- .../features/panel/partials/metrics_tab.html | 105 +++++++++++++----- .../app/features/plugins/plugin_edit_ctrl.ts | 9 +- .../plugins/datasource/graphite/datasource.ts | 13 +++ .../app/plugins/datasource/graphite/help.md | 32 ++++++ .../plugins/datasource/graphite/plugin.json | 2 + .../plugins/datasource/influxdb/plugin.json | 1 + public/sass/_variables.dark.scss | 2 +- public/sass/components/_gf-form.scss | 2 +- public/sass/components/_infobox.scss | 37 ++++-- 15 files changed, 231 insertions(+), 75 deletions(-) create mode 100644 public/app/plugins/datasource/graphite/help.md diff --git a/pkg/api/api.go b/pkg/api/api.go index d4d23b17ef4..4024655775e 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -209,7 +209,7 @@ func (hs *HttpServer) registerRoutes() { r.Get("/plugins", wrap(GetPluginList)) r.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingById)) - r.Get("/plugins/:pluginId/readme", wrap(GetPluginReadme)) + r.Get("/plugins/:pluginId/markdown/:name", wrap(GetPluginMarkdown)) r.Group("/plugins", func() { r.Get("/:pluginId/dashboards/", wrap(GetPluginDashboards)) diff --git a/pkg/api/plugins.go b/pkg/api/plugins.go index fe7122f8557..042c03f9832 100644 --- a/pkg/api/plugins.go +++ b/pkg/api/plugins.go @@ -147,15 +147,16 @@ func GetPluginDashboards(c *middleware.Context) Response { } } -func GetPluginReadme(c *middleware.Context) Response { +func GetPluginMarkdown(c *middleware.Context) Response { pluginId := c.Params(":pluginId") + name := c.Params(":name") - if content, err := plugins.GetPluginReadme(pluginId); err != nil { + if content, err := plugins.GetPluginMarkdown(pluginId, name); err != nil { if notfound, ok := err.(plugins.PluginNotFoundError); ok { return ApiError(404, notfound.Error(), nil) } - return ApiError(500, "Could not get readme", err) + return ApiError(500, "Could not get markdown file", err) } else { return Respond(200, content) } diff --git a/pkg/plugins/datasource_plugin.go b/pkg/plugins/datasource_plugin.go index 8cb0748936b..e46c2709649 100644 --- a/pkg/plugins/datasource_plugin.go +++ b/pkg/plugins/datasource_plugin.go @@ -1,15 +1,24 @@ package plugins -import "encoding/json" +import ( + "encoding/json" + "os" + "path/filepath" +) type DataSourcePlugin struct { FrontendPluginBase - Annotations bool `json:"annotations"` - Metrics bool `json:"metrics"` - Alerting bool `json:"alerting"` - BuiltIn bool `json:"builtIn"` - Mixed bool `json:"mixed"` - Routes []*AppPluginRoute `json:"routes"` + Annotations bool `json:"annotations"` + Metrics bool `json:"metrics"` + Alerting bool `json:"alerting"` + MinInterval bool `json:"minInterval,omitempty"` + CacheTimeout bool `json:"cacheTimeout,omitempty"` + MaxDataPoints bool `json:"maxDataPoints,omitempty"` + BuiltIn bool `json:"builtIn,omitempty"` + Mixed bool `json:"mixed,omitempty"` + HasHelp bool `json:"hasHelp,omitempty"` + + Routes []*AppPluginRoute `json:"-"` } func (p *DataSourcePlugin) Load(decoder *json.Decoder, pluginDir string) error { @@ -21,6 +30,15 @@ func (p *DataSourcePlugin) Load(decoder *json.Decoder, pluginDir string) error { return err } + // look for help markdown + helpPath := filepath.Join(p.PluginDir, "HELP.md") + if _, err := os.Stat(helpPath); os.IsNotExist(err) { + helpPath = filepath.Join(p.PluginDir, "help.md") + } + if _, err := os.Stat(helpPath); err == nil { + p.HasHelp = true + } + DataSources[p.Id] = p return nil } diff --git a/pkg/plugins/models.go b/pkg/plugins/models.go index fd04852f2f4..541b37c8a8a 100644 --- a/pkg/plugins/models.go +++ b/pkg/plugins/models.go @@ -38,8 +38,8 @@ type PluginBase struct { Includes []*PluginInclude `json:"includes"` Module string `json:"module"` BaseUrl string `json:"baseUrl"` - HideFromList bool `json:"hideFromList"` - State string `json:"state"` + HideFromList bool `json:"hideFromList,omitempty"` + State string `json:"state,omitempty"` IncludedInAppId string `json:"-"` PluginDir string `json:"-"` @@ -48,9 +48,6 @@ type PluginBase struct { GrafanaNetVersion string `json:"-"` GrafanaNetHasUpdate bool `json:"-"` - - // cache for readme file contents - Readme []byte `json:"-"` } func (pb *PluginBase) registerPlugin(pluginDir string) error { diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go index b6c3639cbbf..885bd5c9e03 100644 --- a/pkg/plugins/plugins.go +++ b/pkg/plugins/plugins.go @@ -3,6 +3,7 @@ package plugins import ( "encoding/json" "errors" + "fmt" "io/ioutil" "os" "path" @@ -166,30 +167,24 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error { return loader.Load(jsonParser, currentDir) } -func GetPluginReadme(pluginId string) ([]byte, error) { +func GetPluginMarkdown(pluginId string, name string) ([]byte, error) { plug, exists := Plugins[pluginId] if !exists { return nil, PluginNotFoundError{pluginId} } - if plug.Readme != nil { - return plug.Readme, nil + path := filepath.Join(plug.PluginDir, fmt.Sprintf("%s.md", strings.ToUpper(name))) + if _, err := os.Stat(path); os.IsNotExist(err) { + path = filepath.Join(plug.PluginDir, fmt.Sprintf("%s.md", strings.ToLower(name))) } - readmePath := filepath.Join(plug.PluginDir, "README.md") - if _, err := os.Stat(readmePath); os.IsNotExist(err) { - readmePath = filepath.Join(plug.PluginDir, "readme.md") + if _, err := os.Stat(path); os.IsNotExist(err) { + return make([]byte, 0), nil } - if _, err := os.Stat(readmePath); os.IsNotExist(err) { - plug.Readme = make([]byte, 0) - return plug.Readme, nil - } - - if readmeBytes, err := ioutil.ReadFile(readmePath); err != nil { + if data, err := ioutil.ReadFile(path); err != nil { return nil, err } else { - plug.Readme = readmeBytes - return plug.Readme, nil + return data, nil } } diff --git a/public/app/features/panel/metrics_tab.ts b/public/app/features/panel/metrics_tab.ts index 82fa3be99e7..25957ac35ae 100644 --- a/public/app/features/panel/metrics_tab.ts +++ b/public/app/features/panel/metrics_tab.ts @@ -2,6 +2,7 @@ import _ from 'lodash'; import {DashboardModel} from '../dashboard/model'; +import Remarkable from 'remarkable'; export class MetricsTabCtrl { dsName: string; @@ -14,9 +15,16 @@ export class MetricsTabCtrl { panelDsValue: any; addQueryDropdown: any; queryTroubleshooterOpen: boolean; + helpOpen: boolean; + hasHelp: boolean; + helpHtml: string; + hasMinInterval: boolean; + hasCacheTimeout: boolean; + hasMaxDataPoints: boolean; + animateStart: boolean; /** @ngInject */ - constructor($scope, private uiSegmentSrv, private datasourceSrv) { + constructor($scope, private $sce, private datasourceSrv, private backendSrv, private $timeout) { this.panelCtrl = $scope.ctrl; $scope.ctrl = this; @@ -34,6 +42,14 @@ export class MetricsTabCtrl { this.addQueryDropdown = {text: 'Add Query', value: null, fake: true}; // update next ref id this.panelCtrl.nextRefId = this.dashboard.getNextQueryLetter(this.panel); + this.updateDatasourceOptions(); + } + + updateDatasourceOptions() { + this.hasHelp = this.current.meta.hasHelp; + this.hasMinInterval = this.current.meta.minInterval === true; + this.hasCacheTimeout = this.current.meta.cacheTimeout === true; + this.hasMaxDataPoints = this.current.meta.maxDataPoints === true; } getOptions(includeBuiltin) { @@ -51,6 +67,7 @@ export class MetricsTabCtrl { this.current = option.datasource; this.panelCtrl.setDatasource(option.datasource); + this.updateDatasourceOptions(); } addMixedQuery(option) { @@ -67,6 +84,19 @@ export class MetricsTabCtrl { this.panelCtrl.addQuery({isNew: true}); } + toggleHelp() { + this.animateStart = false; + this.helpOpen = !this.helpOpen; + this.backendSrv.get(`/api/plugins/${this.current.meta.id}/markdown/help`).then(res => { + var md = new Remarkable(); + this.helpHtml = this.$sce.trustAsHtml(md.render(res)); + + this.$timeout(() => { + this.animateStart = true; + }, 1); + }); + } + toggleQueryTroubleshooter() { this.queryTroubleshooterOpen = !this.queryTroubleshooterOpen; } diff --git a/public/app/features/panel/partials/metrics_tab.html b/public/app/features/panel/partials/metrics_tab.html index 8ceee90e7ff..53033d0e241 100644 --- a/public/app/features/panel/partials/metrics_tab.html +++ b/public/app/features/panel/partials/metrics_tab.html @@ -11,41 +11,90 @@ on-change="ctrl.datasourceChanged($option)"> -
- - +
+ + A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example 1m if your data is written every minute. Access auto interval via variable $__interval for time range string and $__interval_ms for numeric variable that can be used in math expressions. -
+
+
+ + + + If your time series store has a query cache this option can override the default + cache timeout. Specify a numeric value in seconds. + +
+
+ + + + The maximum data points the query should return. For graphs this + is automatically set to one data point per pixel. + +
+
-
- +
+
- -
-
+ + + +
+
+ + + +
+
-
- - - - +
+ + + +
@@ -56,16 +105,16 @@ {{ctrl.panelCtrl.nextRefId}} - + - -
-
+ +
+ diff --git a/public/app/features/plugins/plugin_edit_ctrl.ts b/public/app/features/plugins/plugin_edit_ctrl.ts index 4910ee95490..50a9138e7f8 100644 --- a/public/app/features/plugins/plugin_edit_ctrl.ts +++ b/public/app/features/plugins/plugin_edit_ctrl.ts @@ -3,6 +3,7 @@ import angular from 'angular'; import _ from 'lodash'; import appEvents from 'app/core/app_events'; +import Remarkable from 'remarkable'; export class PluginEditCtrl { model: any; @@ -67,11 +68,9 @@ export class PluginEditCtrl { } initReadme() { - return this.backendSrv.get(`/api/plugins/${this.pluginId}/readme`).then(res => { - return System.import('remarkable').then(Remarkable => { - var md = new Remarkable(); - this.readmeHtml = this.$sce.trustAsHtml(md.render(res)); - }); + return this.backendSrv.get(`/api/plugins/${this.pluginId}/markdown/readme`).then(res => { + var md = new Remarkable(); + this.readmeHtml = this.$sce.trustAsHtml(md.render(res)); }); } diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts index 59ba485814b..61d97b38650 100644 --- a/public/app/plugins/datasource/graphite/datasource.ts +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -16,6 +16,19 @@ export function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv this.withCredentials = instanceSettings.withCredentials; this.render_method = instanceSettings.render_method || 'POST'; + this.getQueryOptionsInfo = function() { + return { + "maxDataPoints": true, + "cacheTimeout": true, + "links": [ + { + text: "Help", + url: "http://docs.grafana.org/features/datasources/graphite/#using-graphite-in-grafana" + } + ] + }; + }; + this.query = function(options) { var graphOptions = { from: this.translateTime(options.rangeRaw.from, false), diff --git a/public/app/plugins/datasource/graphite/help.md b/public/app/plugins/datasource/graphite/help.md new file mode 100644 index 00000000000..c57d89985d3 --- /dev/null +++ b/public/app/plugins/datasource/graphite/help.md @@ -0,0 +1,32 @@ +#### Get Shorter legend names + +- alias() function to specify a custom series name< +- aliasByNode(2) to alias by a specific part of your metric path +- aliasByNode(2, -1) you can add multiple segment paths, and use negative index +- groupByNode(2, 'sum') is useful if you have 2 wildcards in your metric path and want to sumSeries and group by + +#### Series as parameter + +- Some graphite functions allow you to have many series arguments +- Use #[A-Z] to use a graphite query as parameter to a function +- Examples: + - asPercent(#A, #B) + - prod.srv-01.counters.count - asPercent(#A) : percentage of count in comparison with A query + - prod.srv-01.counters.count - sumSeries(#A) : sum count and series A + - divideSeries(#A, #B) + +If a query is added only to be used as a parameter, hide it from the graph with the eye icon + +#### Max data points +- Every graphite request is issued with a maxDataPoints parameter +- Graphite uses this parameter to consolidate the real number of values down to this number +- If there are more real values, then by default they will be consolidated using averages +- This could hide real peaks and max values in your series +- You can change how point consolidation is made using the consolidateBy graphite function +- Point consolidation will effect series legend values (min,max,total,current) +- if you override maxDataPoint and set a high value performance can be severely effected + +#### Documentation links: + +- [Grafana's Graphite Documentation](http://docs.grafana.org/features/datasources/graphite) +- [Official Graphite Documentation](https://graphite.readthedocs.io) diff --git a/public/app/plugins/datasource/graphite/plugin.json b/public/app/plugins/datasource/graphite/plugin.json index 85009cfd294..6802366691e 100644 --- a/public/app/plugins/datasource/graphite/plugin.json +++ b/public/app/plugins/datasource/graphite/plugin.json @@ -10,6 +10,8 @@ "metrics": true, "alerting": true, "annotations": true, + "maxDataPoints": true, + "cacheTimeout": true, "info": { "author": { diff --git a/public/app/plugins/datasource/influxdb/plugin.json b/public/app/plugins/datasource/influxdb/plugin.json index 56587f91c78..097b6594536 100644 --- a/public/app/plugins/datasource/influxdb/plugin.json +++ b/public/app/plugins/datasource/influxdb/plugin.json @@ -7,6 +7,7 @@ "metrics": true, "annotations": true, "alerting": true, + "minInterval": true, "info": { "author": { diff --git a/public/sass/_variables.dark.scss b/public/sass/_variables.dark.scss index 9ce6b2fd93f..131e52f3f02 100644 --- a/public/sass/_variables.dark.scss +++ b/public/sass/_variables.dark.scss @@ -276,7 +276,7 @@ $card-background-hover: linear-gradient(135deg, #343434, #262626); $card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, .1), 1px 1px 0 0 rgba(0, 0, 0, .3); // info box -$info-box-background: linear-gradient(120deg, #142749, #0e203e); +$info-box-background: linear-gradient(177deg, #006e95, #412078); // footer $footer-link-color: $gray-1; diff --git a/public/sass/components/_gf-form.scss b/public/sass/components/_gf-form.scss index a330cc779a0..a15f91e9ed3 100644 --- a/public/sass/components/_gf-form.scss +++ b/public/sass/components/_gf-form.scss @@ -277,7 +277,7 @@ $gf-form-margin: 0.25rem; &--right-absolute { position: absolute; right: $spacer; - top: 8px; + top: 10px; } &--right-normal { diff --git a/public/sass/components/_infobox.scss b/public/sass/components/_infobox.scss index cb80187f655..a7d762c602d 100644 --- a/public/sass/components/_infobox.scss +++ b/public/sass/components/_infobox.scss @@ -1,12 +1,12 @@ -.grafana-info-box::before { - content: "\f05a"; - font-family:'FontAwesome'; - position: absolute; - top: -13px; - left: -8px; - font-size: 20px; - color: $text-color; -} +// .grafana-info-box::before { +// content: "\f05a"; +// font-family:'FontAwesome'; +// position: absolute; +// top: -13px; +// left: -8px; +// font-size: 20px; +// color: $text-color; +// } .grafana-info-box { position: relative; @@ -15,6 +15,7 @@ padding: 1rem; border-radius: 4px; margin-bottom: $spacer; + margin-right: $gf-form-margin; h5 { margin-bottom: $spacer; @@ -26,5 +27,23 @@ a { @extend .external-link; } + + &--animate { + max-height: 0; + overflow: hidden; + } + + &--animate-open { + max-height: 1000px; + transition: max-height 250ms ease-in-out; + } } +.grafana-info-box__close { + text-align: center; + display: block; + color: $link-color !important; + height: 0; + position: relative; + top: -9px; +} From eb0f5cc900e7928ed4a69d244278671aaba16294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 31 Aug 2017 16:38:49 +0200 Subject: [PATCH 3/6] ux: progress on metrics tab changes --- public/app/core/core.ts | 1 + public/app/core/utils/outline.js | 32 +++++ public/app/features/panel/metrics_tab.ts | 18 ++- .../features/panel/partials/metrics_tab.html | 113 ++++++++---------- .../app/plugins/datasource/graphite/help.md | 7 +- public/sass/_variables.dark.scss | 2 +- public/sass/components/_gf-form.scss | 4 + public/sass/components/_infobox.scss | 13 +- public/sass/mixins/_animations.scss | 11 ++ 9 files changed, 112 insertions(+), 89 deletions(-) create mode 100644 public/app/core/utils/outline.js diff --git a/public/app/core/core.ts b/public/app/core/core.ts index f7ebb614d2a..6a5702d1f12 100644 --- a/public/app/core/core.ts +++ b/public/app/core/core.ts @@ -20,6 +20,7 @@ import './jquery_extended'; import './partials'; import './components/jsontree/jsontree'; import './components/code_editor/code_editor'; +import './utils/outline'; import {grafanaAppDirective} from './components/grafana_app'; import {sideMenuDirective} from './components/sidemenu/sidemenu'; diff --git a/public/app/core/utils/outline.js b/public/app/core/utils/outline.js new file mode 100644 index 00000000000..f968b3e51f9 --- /dev/null +++ b/public/app/core/utils/outline.js @@ -0,0 +1,32 @@ +// outline.js +// based on http://www.paciellogroup.com/blog/2012/04/how-to-remove-css-outlines-in-an-accessible-manner/ +(function(d) { + "use strict"; + + var style_element = d.createElement('STYLE'), + dom_events = 'addEventListener' in d, + add_event_listener = function(type, callback) { + // Basic cross-browser event handling + if(dom_events){ + d.addEventListener(type, callback); + } else { + d.attachEvent('on' + type, callback); + } + }, + set_css = function(css_text) { + // Handle setting of