diff --git a/CHANGELOG.md b/CHANGELOG.md index d6d3653bcaf..c796577ec66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ * **Templating**: Update panel repeats for variables that change on time refresh, closes [#5021](https://github.com/grafana/grafana/issues/5021) * **Elasticsearch**: Support to set Precision Threshold for Unique Count metric, closes [#4689](https://github.com/grafana/grafana/issues/4689) -# 3.1.1 (unreleased / v3.1.x branch) +# 3.1.1 (2016-08-01) * **IFrame embedding**: Fixed issue of using full iframe height, fixes [#5605](https://github.com/grafana/grafana/issues/5606) * **Panel PNG rendering**: Fixed issue detecting render completion, fixes [#5605](https://github.com/grafana/grafana/issues/5606) * **Elasticsearch**: Fixed issue with templating query and json parse error, fixes [#5615](https://github.com/grafana/grafana/issues/5615) @@ -15,6 +15,7 @@ * **Graphite**: Fixed issue with mixed data sources and Graphite, fixes [#5617](https://github.com/grafana/grafana/issues/5617) * **Templating**: Fixed issue with template variable query was issued multiple times during dashboard load, fixes [#5637](https://github.com/grafana/grafana/issues/5637) * **Zoom**: Fixed issues with zoom in and out on embedded (iframed) panel, fixes [#4489](https://github.com/grafana/grafana/issues/4489), [#5666](https://github.com/grafana/grafana/issues/5666) +* **Templating**: Row/Panel repeat issue when saving dashboard caused dupes to appear, fixes [#5591](https://github.com/grafana/grafana/issues/5591) # 3.1.0 stable (2016-07-12) diff --git a/circle.yml b/circle.yml index ee19b50ee46..ef11d6b038e 100644 --- a/circle.yml +++ b/circle.yml @@ -18,15 +18,13 @@ dependencies: test: override: - # FMT - test -z "$(gofmt -s -l . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)" - # GO VET - go vet ./pkg/... - # Go test - - godep go test -v ./pkg/... - # js tests + # JS tests - npm test - npm run coveralls + # GO tests + - godep go test -v ./pkg/... deployment: master: diff --git a/conf/defaults.ini b/conf/defaults.ini index 6d8ff7c4b2b..11c9ca667c4 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -378,7 +378,7 @@ interval_seconds = 60 # Send internal Grafana metrics to graphite ; [metrics.graphite] ; address = localhost:2003 -; prefix = prod.grafana.%(instance_name)s. +; prefix = service.grafana.%(instance_name)s [grafana_net] url = https://grafana.net diff --git a/conf/sample.ini b/conf/sample.ini index 6abc8ba416d..80ca033f035 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -298,7 +298,7 @@ check_for_updates = true # Metrics available at HTTP API Url /api/metrics [metrics] # Disable / Enable internal metrics -;enabled = true +enabled = true # Publish interval ;interval_seconds = 10 @@ -306,7 +306,7 @@ check_for_updates = true # Send internal metrics to Graphite ; [metrics.graphite] ; address = localhost:2003 -; prefix = prod.grafana.%(instance_name)s. +; prefix = service.grafana.%(instance_name)s #################################### Internal Grafana Metrics ########################## # Url used to to import dashboards directly from Grafana.net diff --git a/docs/sources/guides/gettingstarted.md b/docs/sources/guides/gettingstarted.md index c92f30098ab..004b1fcdadb 100644 --- a/docs/sources/guides/gettingstarted.md +++ b/docs/sources/guides/gettingstarted.md @@ -5,7 +5,7 @@ page_keywords: grafana, guide, documentation --- # Getting started -This guide will help you get started and acquainted with Grafana. It assumes you have a working Grafana 2.x instance, and have added at least one [Data Source](/datasources/overview). +This guide will help you get started and acquainted with Grafana. It assumes you have a working Grafana server up and running and have added at least one [Data Source](/datasources/overview). ## Beginner guides Watch the 10min [beginners guide to building dashboards](https://www.youtube.com/watch?v=sKNZMtoSHN4&index=7&list=PLDGkOdUX1Ujo3wHw9-z5Vo12YLqXRjzg2) to get a quick intro to setting up Dashboards and Panels. diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md index d0ce39cf482..931d123c68b 100644 --- a/docs/sources/installation/configuration.md +++ b/docs/sources/installation/configuration.md @@ -76,7 +76,7 @@ The IP address to bind to. If empty will bind to all interfaces The port to bind to, defaults to `3000`. To use port 80 you need to either give the Grafana binary permission for example: - $ sudo setcap 'cap_net_bind_service=+ep' /opt/grafana/current/grafana + $ sudo setcap 'cap_net_bind_service=+ep' /usr/sbin/grafana-server Or redirect port 80 to the Grafana port using: @@ -477,3 +477,14 @@ Format ``:port ### prefix Graphite metric prefix. Defaults to `prod.grafana.%(instance_name)s.` +## [snapshots] + +### external_enabled +Set to false to disable external snapshot publish endpoint (default true) + +### external_snapshot_url +Set root url to a Grafana instance where you want to publish external snapshots (defaults to https://snapshots-origin.raintank.io) + +### external_snapshot_name +Set name for external snapshot button. Defaults to `Publish to snapshot.raintank.io` + diff --git a/docs/sources/installation/debian.md b/docs/sources/installation/debian.md index 360d43512f0..363a4759cce 100644 --- a/docs/sources/installation/debian.md +++ b/docs/sources/installation/debian.md @@ -10,13 +10,13 @@ page_keywords: grafana, installation, debian, ubuntu, guide Description | Download ------------ | ------------- -Stable .deb for Debian-based Linux | [3.1.0](https://grafanarel.s3.amazonaws.com/builds/grafana_3.1.0-1468321182_amd64.deb) +Stable .deb for Debian-based Linux | [3.1.1 (x86-64 deb)](https://grafanarel.s3.amazonaws.com/builds/grafana_3.1.1-1470047149_amd64.deb) ## Install Stable - $ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.1.0-1468321182_amd64.deb + $ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.1.1-1470047149_amd64.deb $ sudo apt-get install -y adduser libfontconfig - $ sudo dpkg -i grafana_3.1.0-1468321182_amd64.deb + $ sudo dpkg -i grafana_3.1.1-1470047149_amd64.deb ## APT Repository diff --git a/docs/sources/installation/mac.md b/docs/sources/installation/mac.md index e65c2823666..b68cadbd948 100644 --- a/docs/sources/installation/mac.md +++ b/docs/sources/installation/mac.md @@ -11,28 +11,17 @@ Installation can be done using [homebrew](http://brew.sh/) Install latest stable: ``` -brew install grafana/grafana/grafana +brew update +brew install grafana ``` To start grafana look at the command printed after the homebrew install completes. -You can also add the grafana as tap. - -``` -brew tap grafana/grafana -brew install grafana -``` - -Install latest unstable from master: - -``` -brew install --HEAD grafana/grafana/grafana -``` - To upgrade use the reinstall command ``` -brew reinstall --HEAD grafana/grafana/grafana +brew update +brew reinstall grafana ``` diff --git a/docs/sources/installation/rpm.md b/docs/sources/installation/rpm.md index c08521a7538..d1a402a08c9 100644 --- a/docs/sources/installation/rpm.md +++ b/docs/sources/installation/rpm.md @@ -10,24 +10,24 @@ page_keywords: grafana, installation, centos, fedora, opensuse, redhat, guide Description | Download ------------ | ------------- -Stable .RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [3.1.0 (x86-64 rpm)](https://grafanarel.s3.amazonaws.com/builds/grafana-3.1.0-1468321182.x86_64.rpm) +Stable .RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [3.1.1 (x86-64 rpm)](https://grafanarel.s3.amazonaws.com/builds/grafana-3.1.1-1470047149.x86_64.rpm) ## Install Latest Stable You can install Grafana using Yum directly. - $ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-3.1.0-1468321182.x86_64.rpm + $ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-3.1.1-1470047149.x86_64.rpm Or install manually using `rpm`. #### On CentOS / Fedora / Redhat: $ sudo yum install initscripts fontconfig - $ sudo rpm -Uvh grafana-3.1.0-1468321182.x86_64.rpm + $ sudo rpm -Uvh grafana-3.1.1-1470047149.x86_64.rpm #### On OpenSuse: - $ sudo rpm -i --nodeps grafana-3.1.0-1468321182.x86_64.rpm + $ sudo rpm -i --nodeps grafana-3.1.1-1470047149.x86_64.rpm ## Install via YUM Repository diff --git a/docs/sources/installation/windows.md b/docs/sources/installation/windows.md index 88c92d2ffed..7dc4ce6d5d3 100644 --- a/docs/sources/installation/windows.md +++ b/docs/sources/installation/windows.md @@ -10,7 +10,7 @@ page_keywords: grafana, installation, windows guide Description | Download ------------ | ------------- -Stable Zip package for Windows | [grafana.3.1.0.windows-x64.zip](https://grafanarel.s3.amazonaws.com/winbuilds/dist/grafana-3.1.0.windows-x64.zip) +Stable Zip package for Windows | [grafana.3.1.1.windows-x64.zip](https://grafanarel.s3.amazonaws.com/winbuilds/dist/grafana-3.1.1.windows-x64.zip) ## Configure diff --git a/docs/sources/reference/playlist.md b/docs/sources/reference/playlist.md index 5d546b1a3df..f35767625c5 100644 --- a/docs/sources/reference/playlist.md +++ b/docs/sources/reference/playlist.md @@ -14,7 +14,7 @@ Since Grafana automatically scales Dashboards to any resolution they're perfect The Playlist feature can be accessed from Grafana's sidemenu. Click the 'Playlist' button from the sidemenu to access the Playlist functionality. When 'Playlist' button is clicked, playlist view will open up showing saved playlists and an option to create new playlists. - + Click on "New Playlist" button to create a new playlist. Firstly, name your playlist and configure a time interval for Grafana to wait on a particular Dashboard before advancing to the next one on the Playlist. diff --git a/latest.json b/latest.json index f27001ccc41..237cb9cfac0 100644 --- a/latest.json +++ b/latest.json @@ -1,4 +1,4 @@ { - "stable": "3.1.0", - "testing": "3.1.0" + "stable": "3.1.1", + "testing": "3.1.1" } diff --git a/packaging/publish/publish.sh b/packaging/publish/publish.sh index 2ab50095695..291222a80e9 100755 --- a/packaging/publish/publish.sh +++ b/packaging/publish/publish.sh @@ -1,20 +1,20 @@ #! /usr/bin/env bash -deb_ver=3.1.0-1466666977beta1 -rpm_ver=3.1.0-1466666977beta1 +deb_ver=3.1.1-1470047149 +rpm_ver=3.1.1-1470047149 -# wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb +wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb -# package_cloud push grafana/stable/debian/jessie grafana_${deb_ver}_amd64.deb -# package_cloud push grafana/stable/debian/wheezy grafana_${deb_ver}_amd64.deb +package_cloud push grafana/stable/debian/jessie grafana_${deb_ver}_amd64.deb +package_cloud push grafana/stable/debian/wheezy grafana_${deb_ver}_amd64.deb package_cloud push grafana/testing/debian/jessie grafana_${deb_ver}_amd64.deb package_cloud push grafana/testing/debian/wheezy grafana_${deb_ver}_amd64.deb -# wget https://grafanarel.s3.amazonaws.com/builds/grafana-${rpm_ver}.x86_64.rpm +wget https://grafanarel.s3.amazonaws.com/builds/grafana-${rpm_ver}.x86_64.rpm package_cloud push grafana/testing/el/6 grafana-${rpm_ver}.x86_64.rpm package_cloud push grafana/testing/el/7 grafana-${rpm_ver}.x86_64.rpm -# package_cloud push grafana/stable/el/7 grafana-${rpm_ver}.x86_64.rpm -# package_cloud push grafana/stable/el/6 grafana-${rpm_ver}.x86_64.rpm +package_cloud push grafana/stable/el/7 grafana-${rpm_ver}.x86_64.rpm +package_cloud push grafana/stable/el/6 grafana-${rpm_ver}.x86_64.rpm diff --git a/pkg/metrics/graphite.go b/pkg/metrics/graphite.go index a232b97905e..2b3da3e490c 100644 --- a/pkg/metrics/graphite.go +++ b/pkg/metrics/graphite.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "net" + "strings" "time" "github.com/grafana/grafana/pkg/log" @@ -20,14 +21,22 @@ type GraphitePublisher struct { func CreateGraphitePublisher() (*GraphitePublisher, error) { graphiteSection, err := setting.Cfg.GetSection("metrics.graphite") if err != nil { - return nil, nil + return nil, err } publisher := &GraphitePublisher{} publisher.prevCounts = make(map[string]int64) publisher.protocol = "tcp" publisher.address = graphiteSection.Key("address").MustString("localhost:2003") - publisher.prefix = graphiteSection.Key("prefix").MustString("service.grafana.%(instance_name)s") + + safeInstanceName := strings.Replace(setting.InstanceName, ".", "_", -1) + prefix := graphiteSection.Key("prefix").Value() + + if prefix == "" { + prefix = "service.grafana.%(instance_name)s" + } + + publisher.prefix = strings.Replace(prefix, "%(instance_name)s", safeInstanceName, -1) return publisher, nil } diff --git a/pkg/metrics/graphite_test.go b/pkg/metrics/graphite_test.go new file mode 100644 index 00000000000..31620bdd161 --- /dev/null +++ b/pkg/metrics/graphite_test.go @@ -0,0 +1,55 @@ +package metrics + +import ( + "testing" + + "github.com/grafana/grafana/pkg/setting" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestGraphitePublisher(t *testing.T) { + + Convey("Test graphite prefix replacement", t, func() { + var err error + err = setting.NewConfigContext(&setting.CommandLineArgs{ + HomePath: "../../", + }) + + So(err, ShouldBeNil) + + sec, err := setting.Cfg.NewSection("metrics.graphite") + sec.NewKey("prefix", "service.grafana.%(instance_name)s") + sec.NewKey("address", "localhost:2003") + + So(err, ShouldBeNil) + + setting.InstanceName = "hostname.with.dots.com" + publisher, err := CreateGraphitePublisher() + + So(err, ShouldBeNil) + So(publisher, ShouldNotBeNil) + + So(publisher.prefix, ShouldEqual, "service.grafana.hostname_with_dots_com") + }) + + Convey("Test graphite publisher default values", t, func() { + var err error + err = setting.NewConfigContext(&setting.CommandLineArgs{ + HomePath: "../../", + }) + + So(err, ShouldBeNil) + + _, err = setting.Cfg.NewSection("metrics.graphite") + + setting.InstanceName = "hostname.with.dots.com" + publisher, err := CreateGraphitePublisher() + + So(err, ShouldBeNil) + So(publisher, ShouldNotBeNil) + + So(publisher.prefix, ShouldEqual, "service.grafana.hostname_with_dots_com") + So(publisher.address, ShouldEqual, "localhost:2003") + }) +} diff --git a/public/app/features/dashboard/dynamicDashboardSrv.js b/public/app/features/dashboard/dynamicDashboardSrv.js deleted file mode 100644 index f131b47b557..00000000000 --- a/public/app/features/dashboard/dynamicDashboardSrv.js +++ /dev/null @@ -1,182 +0,0 @@ -define([ - 'angular', - 'lodash', -], -function (angular, _) { - 'use strict'; - - var module = angular.module('grafana.services'); - - module.service('dynamicDashboardSrv', function() { - var self = this; - - this.init = function(dashboard) { - if (dashboard.snapshot) { return; } - - this.iteration = new Date().getTime(); - this.process(dashboard); - }; - - this.update = function(dashboard) { - if (dashboard.snapshot) { return; } - - this.iteration = this.iteration + 1; - this.process(dashboard); - }; - - this.process = function(dashboard) { - if (dashboard.templating.list.length === 0) { return; } - this.dashboard = dashboard; - - var i, j, row, panel; - for (i = 0; i < this.dashboard.rows.length; i++) { - row = this.dashboard.rows[i]; - // handle row repeats - if (row.repeat) { - this.repeatRow(row, i); - } - // clean up old left overs - else if (row.repeatRowId && row.repeatIteration !== this.iteration) { - this.dashboard.rows.splice(i, 1); - i = i - 1; - continue; - } - - // repeat panels - for (j = 0; j < row.panels.length; j++) { - panel = row.panels[j]; - if (panel.repeat) { - this.repeatPanel(panel, row); - } - // clean up old left overs - else if (panel.repeatPanelId && panel.repeatIteration !== this.iteration) { - row.panels = _.without(row.panels, panel); - j = j - 1; - } else if (row.repeat || row.repeatRowId) { - continue; - } else if (!_.isEmpty(panel.scopedVars) && panel.repeatIteration !== this.iteration) { - panel.scopedVars = {}; - } - } - } - }; - - // returns a new row clone or reuses a clone from previous iteration - this.getRowClone = function(sourceRow, repeatIndex, sourceRowIndex) { - if (repeatIndex === 0) { - return sourceRow; - } - - var i, panel, row, copy; - var sourceRowId = sourceRowIndex + 1; - - // look for row to reuse - for (i = 0; i < this.dashboard.rows.length; i++) { - row = this.dashboard.rows[i]; - if (row.repeatRowId === sourceRowId && row.repeatIteration !== this.iteration) { - copy = row; - break; - } - } - - if (!copy) { - copy = angular.copy(sourceRow); - this.dashboard.rows.splice(sourceRowIndex + repeatIndex, 0, copy); - - // set new panel ids - for (i = 0; i < copy.panels.length; i++) { - panel = copy.panels[i]; - panel.id = this.dashboard.getNextPanelId(); - } - } - - copy.repeat = null; - copy.repeatRowId = sourceRowId; - copy.repeatIteration = this.iteration; - return copy; - }; - - // returns a new row clone or reuses a clone from previous iteration - this.repeatRow = function(row, rowIndex) { - var variables = this.dashboard.templating.list; - var variable = _.findWhere(variables, {name: row.repeat}); - if (!variable) { - return; - } - - var selected, copy, i, panel; - if (variable.current.text === 'All') { - selected = variable.options.slice(1, variable.options.length); - } else { - selected = _.filter(variable.options, {selected: true}); - } - - _.each(selected, function(option, index) { - copy = self.getRowClone(row, index, rowIndex); - copy.scopedVars = {}; - copy.scopedVars[variable.name] = option; - - for (i = 0; i < copy.panels.length; i++) { - panel = copy.panels[i]; - panel.scopedVars = {}; - panel.scopedVars[variable.name] = option; - } - }, this); - }; - - this.getPanelClone = function(sourcePanel, row, index) { - // if first clone return source - if (index === 0) { - return sourcePanel; - } - - var i, tmpId, panel, clone; - - // first try finding an existing clone to use - for (i = 0; i < row.panels.length; i++) { - panel = row.panels[i]; - if (panel.repeatIteration !== this.iteration && panel.repeatPanelId === sourcePanel.id) { - clone = panel; - break; - } - } - - if (!clone) { - clone = { id: this.dashboard.getNextPanelId() }; - row.panels.push(clone); - } - - // save id - tmpId = clone.id; - // copy properties from source - angular.copy(sourcePanel, clone); - // restore id - clone.id = tmpId; - clone.repeatIteration = this.iteration; - clone.repeatPanelId = sourcePanel.id; - clone.repeat = null; - return clone; - }; - - this.repeatPanel = function(panel, row) { - var variables = this.dashboard.templating.list; - var variable = _.findWhere(variables, {name: panel.repeat}); - if (!variable) { return; } - - var selected; - if (variable.current.text === 'All') { - selected = variable.options.slice(1, variable.options.length); - } else { - selected = _.filter(variable.options, {selected: true}); - } - - _.each(selected, function(option, index) { - var copy = self.getPanelClone(panel, row, index); - copy.span = Math.max(12 / selected.length, panel.minSpan); - copy.scopedVars = copy.scopedVars || {}; - copy.scopedVars[variable.name] = option; - }); - }; - - }); -}); diff --git a/public/app/features/dashboard/dynamic_dashboard_srv.ts b/public/app/features/dashboard/dynamic_dashboard_srv.ts index 3a2405b0e66..790c7310456 100644 --- a/public/app/features/dashboard/dynamic_dashboard_srv.ts +++ b/public/app/features/dashboard/dynamic_dashboard_srv.ts @@ -10,10 +10,6 @@ export class DynamicDashboardSrv { iteration: number; dashboard: any; - constructor() { - this.iteration = new Date().getTime(); - } - init(dashboard) { if (dashboard.snapshot) { return; } this.process(dashboard, {}); @@ -21,14 +17,14 @@ export class DynamicDashboardSrv { update(dashboard) { if (dashboard.snapshot) { return; } - - this.iteration = this.iteration + 1; this.process(dashboard, {}); } process(dashboard, options) { if (dashboard.templating.list.length === 0) { return; } + this.dashboard = dashboard; + this.iteration = (this.iteration || new Date().getTime()) + 1; var cleanUpOnly = options.cleanUpOnly; diff --git a/public/app/features/dashboard/timeSrv.js b/public/app/features/dashboard/timeSrv.js index c6d0b754e85..1e1e6dede59 100644 --- a/public/app/features/dashboard/timeSrv.js +++ b/public/app/features/dashboard/timeSrv.js @@ -20,12 +20,13 @@ define([ this.dashboard = dashboard; this.time = dashboard.time; + this.refresh = dashboard.refresh; this._initTimeFromUrl(); this._parseTime(); - if(this.dashboard.refresh) { - this.setAutoRefresh(this.dashboard.refresh); + if(this.refresh) { + this.setAutoRefresh(this.refresh); } }; @@ -65,6 +66,9 @@ define([ if ($routeParams.to) { this.time.to = this._parseUrlParam($routeParams.to) || this.time.to; } + if ($routeParams.refresh) { + this.refresh = $routeParams.refresh || this.refresh; + } }; this.setAutoRefresh = function (interval) { diff --git a/public/app/features/templating/templateValuesSrv.js b/public/app/features/templating/templateValuesSrv.js index c03e43313f2..44904b07fad 100644 --- a/public/app/features/templating/templateValuesSrv.js +++ b/public/app/features/templating/templateValuesSrv.js @@ -166,7 +166,9 @@ function (angular, _, $, kbn) { if (otherVariable === updatedVariable) { return; } - if (templateSrv.containsVariable(otherVariable.query, updatedVariable.name) || + if ((otherVariable.type === "datasource" && + templateSrv.containsVariable(otherVariable.regex, updatedVariable.name)) || + templateSrv.containsVariable(otherVariable.query, updatedVariable.name) || templateSrv.containsVariable(otherVariable.datasource, updatedVariable.name)) { return self.updateOptions(otherVariable); } diff --git a/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts b/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts index 4d630ff9c40..7358249793e 100644 --- a/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts +++ b/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts @@ -59,8 +59,8 @@ describe('ElasticDatasource', function() { ctx.ds.query({ range: { - from: moment([2015, 4, 30, 10]), - to: moment([2015, 5, 1, 10]) + from: moment.utc([2015, 4, 30, 10]), + to: moment.utc([2015, 5, 1, 10]) }, targets: [{ bucketAggs: [], metrics: [], query: 'escape\\:test' }] }); diff --git a/public/app/plugins/panel/graph/graph_tooltip.js b/public/app/plugins/panel/graph/graph_tooltip.js index a10a8bba68c..03c092fa4ac 100644 --- a/public/app/plugins/panel/graph/graph_tooltip.js +++ b/public/app/plugins/panel/graph/graph_tooltip.js @@ -1,7 +1,8 @@ define([ 'jquery', + 'lodash' ], -function ($) { +function ($, _) { 'use strict'; function GraphTooltip(elem, dashboard, scope, getSeriesFn) { @@ -40,7 +41,7 @@ function ($) { }; this.getMultiSeriesPlotHoverInfo = function(seriesList, pos) { - var value, i, series, hoverIndex; + var value, i, series, hoverIndex, hoverDistance, pointTime; var results = []; //now we know the current X (j) position for X and Y values @@ -60,7 +61,8 @@ function ($) { } hoverIndex = this.findHoverIndexFromData(pos.x, series); - results.time = series.data[hoverIndex][0]; + hoverDistance = Math.abs(pos.x - series.data[hoverIndex][0]); + pointTime = series.data[hoverIndex][0]; if (series.stack) { if (panel.tooltip.value_type === 'individual') { @@ -80,13 +82,23 @@ function ($) { // stacked and steppedLine plots can have series with different length. // Stacked series can increase its length on each new stacked serie if null points found, // to speed the index search we begin always on the last found hoverIndex. - var newhoverIndex = this.findHoverIndexFromDataPoints(pos.x, series, hoverIndex); - results.push({ value: value, hoverIndex: newhoverIndex, color: series.color, label: series.label }); - } else { - results.push({ value: value, hoverIndex: hoverIndex, color: series.color, label: series.label }); + hoverIndex = this.findHoverIndexFromDataPoints(pos.x, series, hoverIndex); + hoverDistance = Math.abs(pos.x - series.data[hoverIndex][0]); } + + results.push({ + value: value, + hoverIndex: hoverIndex, + color: series.color, + label: series.label, + time: pointTime, + distance: hoverDistance + }); } + // Find point which closer to pointer + results.time = _.min(results, 'distance').time; + return results; }; diff --git a/public/sass/_variables.light.scss b/public/sass/_variables.light.scss index 46abc845c8b..aa0f8210e3a 100644 --- a/public/sass/_variables.light.scss +++ b/public/sass/_variables.light.scss @@ -60,8 +60,8 @@ $page-bg: $white; $body-color: $gray-1; $text-color: $gray-1; $text-color-strong: $white; -$text-color-weak: $gray-2; -$text-color-faint: $gray-3; +$text-color-weak: $gray-3; +$text-color-faint: $gray-4; $text-color-emphasis: $dark-5; $text-shadow-strong: none; diff --git a/public/sass/components/_switch.scss b/public/sass/components/_switch.scss index 72b980a772f..131a971fb2f 100644 --- a/public/sass/components/_switch.scss +++ b/public/sass/components/_switch.scss @@ -64,7 +64,7 @@ $switch-height: 1.5rem; input + label::before { font-family: 'FontAwesome'; content: "\f096"; // square-o - color: $text-color-faint; + color: $text-color-weak; transition: transform 0.4s; backface-visibility: hidden; text-shadow: $text-shadow-faint; diff --git a/public/sass/pages/_dashboard.scss b/public/sass/pages/_dashboard.scss index eec86e78234..2d6de7d7584 100644 --- a/public/sass/pages/_dashboard.scss +++ b/public/sass/pages/_dashboard.scss @@ -113,8 +113,6 @@ div.flot-text { .panel { display: inline-block; float: left; - vertical-align: top; - position: relative; } .panel-margin { diff --git a/public/test/specs/dynamicDashboardSrv-specs.js b/public/test/specs/dynamicDashboardSrv-specs.js index a09c3788377..b6d3a6d52fb 100644 --- a/public/test/specs/dynamicDashboardSrv-specs.js +++ b/public/test/specs/dynamicDashboardSrv-specs.js @@ -1,5 +1,5 @@ define([ - 'app/features/dashboard/dynamicDashboardSrv', + 'app/features/dashboard/dynamic_dashboard_srv', 'app/features/dashboard/dashboardSrv' ], function() { 'use strict'; @@ -12,6 +12,7 @@ define([ ctx.setup = function (setupFunc) { beforeEach(module('grafana.services')); + beforeEach(module('grafana.core')); beforeEach(module(function($provide) { $provide.value('contextSrv', { user: { timezone: 'utc'} diff --git a/public/views/index.html b/public/views/index.html index 2d311ac269a..a54d2c0c166 100644 --- a/public/views/index.html +++ b/public/views/index.html @@ -55,7 +55,7 @@
  • - + Community