diff --git a/CHANGELOG.md b/CHANGELOG.md
index ca4e52a39ee..a05bb80ccbc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+# 3.1.0
+
+### Enhancements
+* **Singlestat**: Add support for range to text mappings, closes [#1319](https://github.com/grafana/grafana/issues/1319)
+* **Graph**: Adds sort order options for graph tooltip, closes [#1189](https://github.com/grafana/grafana/issues/1189)
+* **Theme**: Add default theme to config file [#5011](https://github.com/grafana/grafana/pull/5011)
+
# 3.0.2 Stable (unreleased)
* **Templating**: Fixed issue mixing row repeat and panel repeats, fixes [#4988](https://github.com/grafana/grafana/issues/4988)
@@ -9,6 +16,7 @@
# 3.0.1 Stable (2016-05-11)
+### Bug fixes
* **Templating**: Fixed issue with new data source variable not persisting current selected value, fixes [#4934](https://github.com/grafana/grafana/issues/4934)
# 3.0.0-beta7 (2016-05-02)
diff --git a/conf/defaults.ini b/conf/defaults.ini
index f78287619a3..0a4b61fbe78 100644
--- a/conf/defaults.ini
+++ b/conf/defaults.ini
@@ -172,6 +172,9 @@ verify_email_enabled = false
# Background text for the user field on the login page
login_hint = email or username
+# Default UI theme ("dark" or "light")
+default_theme = dark
+
#################################### Anonymous Auth ##########################
[auth.anonymous]
# enable anonymous access
diff --git a/conf/sample.ini b/conf/sample.ini
index 6a26589d40d..7f358b07199 100644
--- a/conf/sample.ini
+++ b/conf/sample.ini
@@ -155,6 +155,9 @@ check_for_updates = true
# Background text for the user field on the login page
;login_hint = email or username
+# Default UI theme ("dark" or "light")
+;default_theme = dark
+
#################################### Anonymous Auth ##########################
[auth.anonymous]
# enable anonymous access
diff --git a/docker/blocks/graphite/fig b/docker/blocks/graphite/fig
index 84da45341e1..4268641d32f 100644
--- a/docker/blocks/graphite/fig
+++ b/docker/blocks/graphite/fig
@@ -8,3 +8,10 @@ graphite:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
+fake-data-gen:
+ image: grafana/fake-data-gen
+ net: bridge
+ environment:
+ FD_DATASOURCE: graphite
+ FD_PORT: 2003
+
diff --git a/docker/blocks/opentsdb/fig b/docker/blocks/opentsdb/fig
index 34bbf4b854c..705d746a864 100644
--- a/docker/blocks/opentsdb/fig
+++ b/docker/blocks/opentsdb/fig
@@ -2,4 +2,10 @@ opentsdb:
image: opower/opentsdb:latest
ports:
- "4242:4242"
-
+
+fake-data-gen:
+ image: grafana/fake-data-gen
+ net: bridge
+ environment:
+ FD_DATASOURCE: opentsdb
+
diff --git a/pkg/services/sqlstore/preferences.go b/pkg/services/sqlstore/preferences.go
index d120c485ed3..65609a9c57c 100644
--- a/pkg/services/sqlstore/preferences.go
+++ b/pkg/services/sqlstore/preferences.go
@@ -5,6 +5,8 @@ import (
"github.com/grafana/grafana/pkg/bus"
m "github.com/grafana/grafana/pkg/models"
+
+ "github.com/grafana/grafana/pkg/setting"
)
func init() {
@@ -26,7 +28,7 @@ func GetPreferencesWithDefaults(query *m.GetPreferencesWithDefaultsQuery) error
}
res := &m.Preferences{
- Theme: "dark",
+ Theme: setting.DefaultTheme,
Timezone: "browser",
HomeDashboardId: 0,
}
diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go
index 2d1bad945eb..b883a8d995d 100644
--- a/pkg/setting/setting.go
+++ b/pkg/setting/setting.go
@@ -88,6 +88,7 @@ var (
AutoAssignOrgRole string
VerifyEmailEnabled bool
LoginHint string
+ DefaultTheme string
// Http auth
AdminUser string
@@ -454,6 +455,7 @@ func NewConfigContext(args *CommandLineArgs) error {
AutoAssignOrgRole = users.Key("auto_assign_org_role").In("Editor", []string{"Editor", "Admin", "Read Only Editor", "Viewer"})
VerifyEmailEnabled = users.Key("verify_email_enabled").MustBool(false)
LoginHint = users.Key("login_hint").String()
+ DefaultTheme = users.Key("default_theme").String()
// anonymous access
AnonymousEnabled = Cfg.Section("auth.anonymous").Key("enabled").MustBool(false)
diff --git a/public/app/plugins/panel/graph/graph_tooltip.js b/public/app/plugins/panel/graph/graph_tooltip.js
index 9ab6369a6b2..0c2136f7d9d 100644
--- a/public/app/plugins/panel/graph/graph_tooltip.js
+++ b/public/app/plugins/panel/graph/graph_tooltip.js
@@ -81,9 +81,9 @@ function ($) {
// 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 });
+ results.push({ value: value, hoverIndex: newhoverIndex, color: series.color, label: series.label });
} else {
- results.push({ value: value, hoverIndex: hoverIndex });
+ results.push({ value: value, hoverIndex: hoverIndex, color: series.color, label: series.label });
}
}
@@ -133,6 +133,18 @@ function ($) {
absoluteTime = dashboard.formatDate(seriesHoverInfo.time, tooltipFormat);
+ // Dynamically reorder the hovercard for the current time point if the
+ // option is enabled.
+ if (panel.tooltip.ordering === 'decreasing') {
+ seriesHoverInfo.sort(function(a, b) {
+ return parseFloat(b.value) - parseFloat(a.value);
+ });
+ } else if (panel.tooltip.ordering === 'increasing') {
+ seriesHoverInfo.sort(function(a, b) {
+ return parseFloat(a.value) - parseFloat(b.value);
+ });
+ }
+
for (i = 0; i < seriesHoverInfo.length; i++) {
hoverInfo = seriesHoverInfo[i];
@@ -150,7 +162,7 @@ function ($) {
value = series.formatValue(hoverInfo.value);
seriesHtml += '
';
seriesHtml += '' + value + '
';
plot.highlight(i, hoverInfo.hoverIndex);
}
diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts
index a11c537b178..2b2e75b5a33 100644
--- a/public/app/plugins/panel/graph/module.ts
+++ b/public/app/plugins/panel/graph/module.ts
@@ -92,6 +92,7 @@ class GraphCtrl extends MetricsPanelCtrl {
tooltip : {
value_type: 'cumulative',
shared: true,
+ ordering: 'alphabetical',
msResolution: false,
},
// time overrides
diff --git a/public/app/plugins/panel/graph/tab_display.html b/public/app/plugins/panel/graph/tab_display.html
index 067651540e2..a33ae0bc67f 100644
--- a/public/app/plugins/panel/graph/tab_display.html
+++ b/public/app/plugins/panel/graph/tab_display.html
@@ -42,23 +42,29 @@
diff --git a/public/app/plugins/panel/singlestat/editor.html b/public/app/plugins/panel/singlestat/editor.html
index fc3c9f69542..0504277a5e1 100644
--- a/public/app/plugins/panel/singlestat/editor.html
+++ b/public/app/plugins/panel/singlestat/editor.html
@@ -204,35 +204,3 @@
-
-
diff --git a/public/app/plugins/panel/singlestat/mappings.html b/public/app/plugins/panel/singlestat/mappings.html
new file mode 100644
index 00000000000..a1105a159dd
--- /dev/null
+++ b/public/app/plugins/panel/singlestat/mappings.html
@@ -0,0 +1,58 @@
+
+
+
Set valuea mappings
+
+
+
diff --git a/public/app/plugins/panel/singlestat/module.ts b/public/app/plugins/panel/singlestat/module.ts
index 9fa3e8e39c6..de272478fbc 100644
--- a/public/app/plugins/panel/singlestat/module.ts
+++ b/public/app/plugins/panel/singlestat/module.ts
@@ -35,6 +35,14 @@ class SingleStatCtrl extends MetricsPanelCtrl {
valueMaps: [
{ value: 'null', op: '=', text: 'N/A' }
],
+ mappingTypes: [
+ {name: 'value to text', value: 1},
+ {name: 'range to text', value: 2},
+ ],
+ rangeMaps: [
+ { from: 'null', to: 'null', text: 'N/A' }
+ ],
+ mappingType: 1,
nullPointMode: 'connected',
valueName: 'avg',
prefixFontSize: '50%',
@@ -73,6 +81,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
onInitEditMode() {
this.fontSizes = ['20%', '30%','50%','70%','80%','100%', '110%', '120%', '150%', '170%', '200%'];
this.addEditorTab('Options', 'public/app/plugins/panel/singlestat/editor.html', 2);
+ this.addEditorTab('Value Mappings', 'public/app/plugins/panel/singlestat/mappings.html', 3);
this.unitFormats = kbn.getUnitFormats();
}
@@ -192,23 +201,45 @@ class SingleStatCtrl extends MetricsPanelCtrl {
}
}
- // check value to text mappings
- for (var i = 0; i < this.panel.valueMaps.length; i++) {
- var map = this.panel.valueMaps[i];
- // special null case
- if (map.value === 'null') {
- if (data.value === null || data.value === void 0) {
+ // check value to text mappings if its enabled
+ if (this.panel.mappingType === 1) {
+ for (var i = 0; i < this.panel.valueMaps.length; i++) {
+ var map = this.panel.valueMaps[i];
+ // special null case
+ if (map.value === 'null') {
+ if (data.value === null || data.value === void 0) {
+ data.valueFormated = map.text;
+ return;
+ }
+ continue;
+ }
+
+ // value/number to text mapping
+ var value = parseFloat(map.value);
+ if (value === data.valueRounded) {
data.valueFormated = map.text;
return;
}
- continue;
}
+ } else if (this.panel.mappingType === 2) {
+ for (var i = 0; i < this.panel.rangeMaps.length; i++) {
+ var map = this.panel.rangeMaps[i];
+ // special null case
+ if (map.from === 'null' && map.to === 'null') {
+ if (data.value === null || data.value === void 0) {
+ data.valueFormated = map.text;
+ return;
+ }
+ continue;
+ }
- // value/number to text mapping
- var value = parseFloat(map.value);
- if (value === data.valueRounded) {
- data.valueFormated = map.text;
- return;
+ // value/number to range mapping
+ var from = parseFloat(map.from);
+ var to = parseFloat(map.to);
+ if (to >= data.valueRounded && from <= data.valueRounded) {
+ data.valueFormated = map.text;
+ return;
+ }
}
}
@@ -227,6 +258,16 @@ class SingleStatCtrl extends MetricsPanelCtrl {
this.panel.valueMaps.push({value: '', op: '=', text: '' });
}
+ removeRangeMap(rangeMap) {
+ var index = _.indexOf(this.panel.rangeMaps, rangeMap);
+ this.panel.rangeMaps.splice(index, 1);
+ this.render();
+ };
+
+ addRangeMap() {
+ this.panel.rangeMaps.push({from: '', to: '', text: ''});
+ }
+
link(scope, elem, attrs, ctrl) {
var $location = this.$location;
var linkSrv = this.linkSrv;
diff --git a/public/app/plugins/panel/singlestat/specs/singlestat-specs.ts b/public/app/plugins/panel/singlestat/specs/singlestat-specs.ts
index dc85454b64a..3d6c565443b 100644
--- a/public/app/plugins/panel/singlestat/specs/singlestat-specs.ts
+++ b/public/app/plugins/panel/singlestat/specs/singlestat-specs.ts
@@ -84,4 +84,29 @@ describe('SingleStatCtrl', function() {
expect(ctx.data.valueFormated).to.be('OK');
});
});
+
+ singleStatScenario('When range to text mapping is specifiedfor first range', function(ctx) {
+ ctx.setup(function() {
+ ctx.datapoints = [[41,50]];
+ ctx.ctrl.panel.mappingType = 2;
+ ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
+ });
+
+ it('Should replace value with text OK', function() {
+ expect(ctx.data.valueFormated).to.be('OK');
+ });
+ });
+
+ singleStatScenario('When range to text mapping is specified for other ranges', function(ctx) {
+ ctx.setup(function() {
+ ctx.datapoints = [[65,75]];
+ ctx.ctrl.panel.mappingType = 2;
+ ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
+ });
+
+ it('Should replace value with text NOT OK', function() {
+ expect(ctx.data.valueFormated).to.be('NOT OK');
+ });
+ });
+
});