Merge branch 'master' into export-dashboard

Conflicts:
	packaging/publish/publish.sh
	public/app/features/dashboard/dynamicDashboardSrv.js
	public/test/specs/dynamicDashboardSrv-specs.js
This commit is contained in:
Torkel Ödegaard 2016-05-12 10:50:37 +02:00
commit 28eae1e7ff
21 changed files with 546 additions and 81 deletions

View File

@ -1,4 +1,9 @@
# 3.0.0 stable (unreleased) # 3.0.2 Stable (unreleased)
* **Templating**: Fixed issue mixing row repeat and panel repeats, fixes [#4988](https://github.com/grafana/grafana/issues/4988)
* **Templating**: Fixed issue detecting dependencies in nested variables, fixes [#4987](https://github.com/grafana/grafana/issues/4987), fixes [#4986](https://github.com/grafana/grafana/issues/4986)
# 3.0.1 Stable (2016-05-11)
* **Templating**: Fixed issue with new data source variable not persisting current selected value, fixes [#4934](https://github.com/grafana/grafana/issues/4934) * **Templating**: Fixed issue with new data source variable not persisting current selected value, fixes [#4934](https://github.com/grafana/grafana/issues/4934)

View File

@ -132,12 +132,10 @@ func readVersionFromPackageJson() {
if len(parts) > 1 { if len(parts) > 1 {
linuxPackageVersion = parts[0] linuxPackageVersion = parts[0]
linuxPackageIteration = parts[1] linuxPackageIteration = parts[1]
if linuxPackageIteration != "" {
// add timestamp to iteration
linuxPackageIteration = fmt.Sprintf("%s%v", linuxPackageIteration, time.Now().Unix())
}
log.Println(fmt.Sprintf("Iteration %v", linuxPackageIteration))
} }
// add timestamp to iteration
linuxPackageIteration = fmt.Sprintf("%d%s", time.Now().Unix(), linuxPackageIteration)
} }
type linuxPackageOptions struct { type linuxPackageOptions struct {

View File

@ -39,12 +39,13 @@ entire experience right within Grafana.
<img src="/img/v3/grafana_net_tour.png"> <img src="/img/v3/grafana_net_tour.png">
A preview of [Grafana.net](http://grafana.net) is launching along with this release. We [Grafana.net](https://grafana.net) offers a central repository where the community can come together to discover, create and
think its the perfect compliment to Grafana. share plugins (data sources, panels, apps) and dashboards.
Grafana.net currently offers a central repository where the community We are also working on a hosted Graphite-compatible data source that will be optimized for use with Grafana.
can come together to discover and share plugins (Data Sources, Panels, Itll be easy to combine your existing data source(s) with this OpenSaaS option. Finally, Grafana.net can
Apps) and Dashboards for Grafana 3.0 and above. also be a hub to manage all your Grafana instances. Youll be able to monitor their health and availability,
perform dashboard backups, and more.
We are also working on a hosted Graphite-compatible Data Source that We are also working on a hosted Graphite-compatible Data Source that
will be optimized for use with Grafana. Itll be easy to combine your will be optimized for use with Grafana. Itll be easy to combine your
@ -65,7 +66,6 @@ Grafana 3.0 comes with a new command line tool called grafana-cli. You
can easily install plugins from Grafana.net with it. For can easily install plugins from Grafana.net with it. For
example: example:
``` ```
grafana-cli install grafana-pie-chart-panel grafana-cli install grafana-pie-chart-panel
``` ```
@ -188,6 +188,33 @@ you can still install manually from [Grafana.net](http://grafana.net)
* KairosDB: This data source has also no longer shipped with Grafana, * KairosDB: This data source has also no longer shipped with Grafana,
you can install it manually from [Grafana.net](http://grafana.net) you can install it manually from [Grafana.net](http://grafana.net)
## Plugin showcase
Discovering and installing plugins is very quick and easy with Grafana 3.0 and [Grafana.net](https://grafana.net). Here
are a couple that I incurage you try!
#### [Clock Panel](https://grafana.net/plugins/grafana-clock-panel)
Support's both current time and count down mode.
<img src="/img/v3/clock_panel.png">
#### [Pie Chart Panel](https://grafana.net/plugins/grafana-piechart-panel)
A simple pie chart panel is now available as an external plugin.
<img src="/img/v3/pie_chart_panel.png">
#### [WorldPing App](https://grafana.net/plugins/raintank-worldping-app)
This is full blown Grafana App that adds new panels, data sources and pages to give
feature rich global performance monitoring directly from your on-prem Grafana.
<img src="/img/v3/wP-Screenshot-dash-web.png">
#### [Zabbix App](https://grafana.net/plugins/alexanderzobnin-zabbix-app)
This app contains the already very pouplar Zabbix data source plugin, 2 dashboards and a triggers panel. It is
created and maintained by [Alexander Zobnin](https://github.com/alexanderzobnin/grafana-zabbix).
<img src="/img/v3/zabbix_app.png">
Checkout the full list of plugins on [Grafana.net](https://grafana.net/plugins)
## CHANGELOG ## CHANGELOG
For a detailed list and link to github issues for everything included For a detailed list and link to github issues for everything included

View File

@ -10,20 +10,13 @@ page_keywords: grafana, installation, debian, ubuntu, guide
Description | Download Description | Download
------------ | ------------- ------------ | -------------
Stable .deb for Debian-based Linux | [grafana_2.6.0_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_2.6.0_amd64.deb) Stable .deb for Debian-based Linux | [grafana_3.0.1_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.1_amd64.deb)
Beta .deb for Debian-based Linux | [grafana_3.0.0-beta71462173753_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta71462173753_amd64.deb)
## Install Stable ## Install Stable
$ wget https://grafanarel.s3.amazonaws.com/builds/grafana_2.6.0_amd64.deb $ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.1_amd64.deb
$ sudo apt-get install -y adduser libfontconfig $ sudo apt-get install -y adduser libfontconfig
$ sudo dpkg -i grafana_2.6.0_amd64.deb $ sudo dpkg -i grafana_3.0.1_amd64.deb
## Install 3.0 Beta
$ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta71462173753_amd64.deb
$ sudo apt-get install -y adduser libfontconfig
$ sudo dpkg -i grafana_3.0.0-beta71462173753_amd64.deb
## APT Repository ## APT Repository

View File

@ -10,43 +10,24 @@ page_keywords: grafana, installation, centos, fedora, opensuse, redhat, guide
Description | Download Description | Download
------------ | ------------- ------------ | -------------
Stable .RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [grafana-2.6.0-1.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-2.6.0-1.x86_64.rpm) Stable .RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [grafana-3.0.1-1.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.1-1.x86_64.rpm)
Beta .RPM for CentOS / Fedor / OpenSuse / Redhat Linux | [grafana-3.0.0-beta71462173753.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta71462173753.x86_64.rpm)
## Install Stable Release from package file ## Install Stable Release from package file
You can install Grafana using Yum directly. You can install Grafana using Yum directly.
$ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-2.6.0-1.x86_64.rpm $ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.1-1.x86_64.rpm
Or install manually using `rpm`. Or install manually using `rpm`.
#### On CentOS / Fedora / Redhat: #### On CentOS / Fedora / Redhat:
$ sudo yum install initscripts fontconfig $ sudo yum install initscripts fontconfig
$ sudo rpm -Uvh grafana-2.6.0-1.x86_64.rpm $ sudo rpm -Uvh grafana-3.0.1-1.x86_64.rpm
#### On OpenSuse: #### On OpenSuse:
$ sudo rpm -i --nodeps grafana-2.6.0-1.x86_64.rpm $ sudo rpm -i --nodeps grafana-3.0.1-1.x86_64.rpm
## Install Beta Release from package file
You can install Grafana using Yum directly.
$ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta71462173753.x86_64.rpm
Or install manually using `rpm`.
#### On CentOS / Fedora / Redhat:
$ sudo yum install initscripts fontconfig
$ sudo rpm -Uvh grafana-3.0.0-beta71462173753.x86_64.rpm
#### On OpenSuse:
$ sudo rpm -i --nodeps grafana-3.0.0-beta71462173753.x86_64.rpm
## Install via YUM Repository ## Install via YUM Repository

View File

@ -1,4 +1,4 @@
{ {
"stable": "2.6.0", "stable": "3.0.1",
"testing": "3.0.0-beta7" "testing": "3.0.1"
} }

View File

@ -4,7 +4,7 @@
"company": "Coding Instinct AB" "company": "Coding Instinct AB"
}, },
"name": "grafana", "name": "grafana",
"version": "3.0.0-beta7", "version": "3.0.2",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "http://github.com/grafana/grafana.git" "url": "http://github.com/grafana/grafana.git"

View File

@ -14,6 +14,6 @@ CONF_DIR=/etc/grafana
CONF_FILE=/etc/grafana/grafana.ini CONF_FILE=/etc/grafana/grafana.ini
RESTART_ON_UPGRADE=false RESTART_ON_UPGRADE=true
PLUGINS_DIR=/var/lib/grafana/plugins PLUGINS_DIR=/var/lib/grafana/plugins

View File

@ -1,7 +1,7 @@
#! /usr/bin/env bash #! /usr/bin/env bash
deb_ver=3.0.0-beta51460658374 deb_ver=3.0.1
rpm_ver=3.0.0-beta51460658374 rpm_ver=3.0.1-1
#rpm_ver=3.0.0-1 #rpm_ver=3.0.0-1
@ -16,7 +16,7 @@ rpm_ver=3.0.0-beta51460658374
#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/6 grafana-${rpm_ver}.x86_64.rpm
package_cloud push grafana/testing/el/7 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-${version}-1.x86_64.rpm package_cloud push grafana/stable/el/7 grafana-${rpm_ver}.x86_64.rpm
# package_cloud push grafana/stable/el/6 grafana-${version}-1.x86_64.rpm package_cloud push grafana/stable/el/6 grafana-${rpm_ver}.x86_64.rpm

View File

@ -14,6 +14,6 @@ CONF_DIR=/etc/grafana
CONF_FILE=/etc/grafana/grafana.ini CONF_FILE=/etc/grafana/grafana.ini
RESTART_ON_UPGRADE=false RESTART_ON_UPGRADE=true
PLUGINS_DIR=/var/lib/grafana/plugins PLUGINS_DIR=/var/lib/grafana/plugins

View File

@ -91,14 +91,14 @@ func checkForUpdates() {
resp2, err := client.Get("https://raw.githubusercontent.com/grafana/grafana/master/latest.json") resp2, err := client.Get("https://raw.githubusercontent.com/grafana/grafana/master/latest.json")
if err != nil { if err != nil {
log.Trace("Failed to get lates.json repo from github: %v", err.Error()) log.Trace("Failed to get latest.json repo from github: %v", err.Error())
return return
} }
defer resp2.Body.Close() defer resp2.Body.Close()
body, err = ioutil.ReadAll(resp2.Body) body, err = ioutil.ReadAll(resp2.Body)
if err != nil { if err != nil {
log.Trace("Update check failed, reading response from github.net, %v", err.Error()) log.Trace("Update check failed, reading response from github.com, %v", err.Error())
return return
} }

View File

@ -0,0 +1,182 @@
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;
});
};
});
});

View File

@ -97,7 +97,8 @@ function (angular, _) {
if (!str) { if (!str) {
return false; return false;
} }
return str.indexOf('$' + variableName) !== -1 || str.indexOf('[[' + variableName + ']]') !== -1; var match = this._regex.exec(str);
return match && (match[1] === variableName || match[2] === variableName);
}; };
this.highlightVariablesAsHtml = function(str) { this.highlightVariablesAsHtml = function(str) {

View File

@ -204,7 +204,7 @@ function (angular, _, kbn) {
} }
if (options.length === 0) { if (options.length === 0) {
options.push({text: 'No datasurces found', value: ''}); options.push({text: 'No data sources found', value: ''});
} }
variable.options = options; variable.options = options;

Binary file not shown.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Binary file not shown.

View File

@ -1,10 +1,10 @@
@font-face { @font-face {
font-family: 'grafana-icons'; font-family: 'grafana-icons';
src: url('../fonts/grafana-icons.eot?h6rv8b'); src: url('../fonts/grafana-icons.eot?okx5td');
src: url('../fonts/grafana-icons.eot?h6rv8b#iefix') format('embedded-opentype'), src: url('../fonts/grafana-icons.eot?okx5td#iefix') format('embedded-opentype'),
url('../fonts/grafana-icons.ttf?h6rv8b') format('truetype'), url('../fonts/grafana-icons.ttf?okx5td') format('truetype'),
url('../fonts/grafana-icons.woff?h6rv8b') format('woff'), url('../fonts/grafana-icons.woff?okx5td') format('woff'),
url('../fonts/grafana-icons.svg?h6rv8b#grafana-icons') format('svg'); url('../fonts/grafana-icons.svg?okx5td#grafana-icons') format('svg');
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@ -61,6 +61,9 @@
.icon-gf-endpoint:before { .icon-gf-endpoint:before {
content: "\e609"; content: "\e609";
} }
.icon-gf-page:before {
content: "\e908";
}
.icon-gf-filter:before { .icon-gf-filter:before {
content: "\e60a"; content: "\e60a";
} }
@ -112,9 +115,6 @@
.icon-gf-save:before { .icon-gf-save:before {
content: "\e614"; content: "\e614";
} }
.icon-gf-settings:before {
content: "\e615";
}
.icon-gf-share:before { .icon-gf-share:before {
content: "\e616"; content: "\e616";
} }
@ -124,10 +124,13 @@
.icon-gf-search:before { .icon-gf-search:before {
content: "\e618"; content: "\e618";
} }
.icon-gf-tag-add:before { .icon-gf-settings:before {
content: "\e615";
}
.icon-gf-add:before {
content: "\e619"; content: "\e619";
} }
.icon-gf-tag-remove:before { .icon-gf-remove:before {
content: "\e61a"; content: "\e61a";
} }
.icon-gf-video:before { .icon-gf-video:before {
@ -169,6 +172,12 @@
.icon-gf-scale:before { .icon-gf-scale:before {
content: "\e906"; content: "\e906";
} }
.icon-gf-pending:before {
content: "\e909";
}
.icon-gf-verified:before {
content: "\e90a";
}
.icon-gf-worldping:before { .icon-gf-worldping:before {
content: "\e627"; content: "\e627";
} }
@ -176,10 +185,3 @@
content: "\e903"; content: "\e903";
} }
.icon-gf-app:before {
content: "\e902";
}
.icon-gf-datasource:before {
content: "\e607";
}

View File

@ -0,0 +1,268 @@
define([
'app/features/dashboard/dynamicDashboardSrv',
'app/features/dashboard/dashboardSrv'
], function() {
'use strict';
function dynamicDashScenario(desc, func) {
describe(desc, function() {
var ctx = {};
ctx.setup = function (setupFunc) {
beforeEach(module('grafana.services'));
beforeEach(module(function($provide) {
$provide.value('contextSrv', {
user: { timezone: 'utc'}
});
}));
beforeEach(inject(function(dynamicDashboardSrv, dashboardSrv) {
ctx.dynamicDashboardSrv = dynamicDashboardSrv;
ctx.dashboardSrv = dashboardSrv;
var model = {
rows: [],
templating: { list: [] }
};
setupFunc(model);
ctx.dash = ctx.dashboardSrv.create(model);
ctx.dynamicDashboardSrv.init(ctx.dash);
ctx.rows = ctx.dash.rows;
}));
};
func(ctx);
});
}
dynamicDashScenario('given dashboard with panel repeat', function(ctx) {
ctx.setup(function(dash) {
dash.rows.push({
panels: [{id: 2, repeat: 'apps'}]
});
dash.templating.list.push({
name: 'apps',
current: {
text: 'se1, se2, se3',
value: ['se1', 'se2', 'se3']
},
options: [
{text: 'se1', value: 'se1', selected: true},
{text: 'se2', value: 'se2', selected: true},
{text: 'se3', value: 'se3', selected: true},
{text: 'se4', value: 'se4', selected: false}
]
});
});
it('should repeat panel one time', function() {
expect(ctx.rows[0].panels.length).to.be(3);
});
it('should mark panel repeated', function() {
expect(ctx.rows[0].panels[0].repeat).to.be('apps');
expect(ctx.rows[0].panels[1].repeatPanelId).to.be(2);
});
it('should set scopedVars on panels', function() {
expect(ctx.rows[0].panels[0].scopedVars.apps.value).to.be('se1');
expect(ctx.rows[0].panels[1].scopedVars.apps.value).to.be('se2');
expect(ctx.rows[0].panels[2].scopedVars.apps.value).to.be('se3');
});
describe('After a second iteration', function() {
var repeatedPanelAfterIteration1;
beforeEach(function() {
repeatedPanelAfterIteration1 = ctx.rows[0].panels[1];
ctx.rows[0].panels[0].fill = 10;
ctx.dynamicDashboardSrv.update(ctx.dash);
});
it('should have reused same panel instances', function() {
expect(ctx.rows[0].panels[1]).to.be(repeatedPanelAfterIteration1);
});
it('reused panel should copy properties from source', function() {
expect(ctx.rows[0].panels[1].fill).to.be(10);
});
it('should have same panel count', function() {
expect(ctx.rows[0].panels.length).to.be(3);
});
});
describe('After a second iteration and selected values reduced', function() {
beforeEach(function() {
ctx.dash.templating.list[0].options[1].selected = false;
ctx.dynamicDashboardSrv.update(ctx.dash);
});
it('should clean up repeated panel', function() {
expect(ctx.rows[0].panels.length).to.be(2);
});
});
describe('After a second iteration and panel repeat is turned off', function() {
beforeEach(function() {
ctx.rows[0].panels[0].repeat = null;
ctx.dynamicDashboardSrv.update(ctx.dash);
});
it('should clean up repeated panel', function() {
expect(ctx.rows[0].panels.length).to.be(1);
});
it('should remove scoped vars from reused panel', function() {
expect(ctx.rows[0].panels[0].scopedVars).to.be.empty();
});
});
});
dynamicDashScenario('given dashboard with row repeat', function(ctx) {
ctx.setup(function(dash) {
dash.rows.push({
repeat: 'servers',
panels: [{id: 2}]
});
dash.rows.push({panels: []});
dash.templating.list.push({
name: 'servers',
current: {
text: 'se1, se2',
value: ['se1', 'se2']
},
options: [
{text: 'se1', value: 'se1', selected: true},
{text: 'se2', value: 'se2', selected: true},
]
});
});
it('should repeat row one time', function() {
expect(ctx.rows.length).to.be(3);
});
it('should keep panel ids on first row', function() {
expect(ctx.rows[0].panels[0].id).to.be(2);
});
it('should keep first row as repeat', function() {
expect(ctx.rows[0].repeat).to.be('servers');
});
it('should clear repeat field on repeated row', function() {
expect(ctx.rows[1].repeat).to.be(null);
});
it('should add scopedVars to rows', function() {
expect(ctx.rows[0].scopedVars.servers.value).to.be('se1');
expect(ctx.rows[1].scopedVars.servers.value).to.be('se2');
});
it('should generate a repeartRowId based on repeat row index', function() {
expect(ctx.rows[1].repeatRowId).to.be(1);
expect(ctx.rows[1].repeatIteration).to.be(ctx.dynamicDashboardSrv.iteration);
});
it('should set scopedVars on row panels', function() {
expect(ctx.rows[0].panels[0].scopedVars.servers.value).to.be('se1');
expect(ctx.rows[1].panels[0].scopedVars.servers.value).to.be('se2');
});
describe('After a second iteration', function() {
var repeatedRowAfterFirstIteration;
beforeEach(function() {
repeatedRowAfterFirstIteration = ctx.rows[1];
ctx.rows[0].height = 500;
ctx.dynamicDashboardSrv.update(ctx.dash);
});
it('should still only have 2 rows', function() {
expect(ctx.rows.length).to.be(3);
});
it.skip('should have updated props from source', function() {
expect(ctx.rows[1].height).to.be(500);
});
it('should reuse row instance', function() {
expect(ctx.rows[1]).to.be(repeatedRowAfterFirstIteration);
});
});
describe('After a second iteration and selected values reduced', function() {
beforeEach(function() {
ctx.dash.templating.list[0].options[1].selected = false;
ctx.dynamicDashboardSrv.update(ctx.dash);
});
it('should remove repeated second row', function() {
expect(ctx.rows.length).to.be(2);
});
});
});
dynamicDashScenario('given dashboard with row repeat and panel repeat', function(ctx) {
ctx.setup(function(dash) {
dash.rows.push({
repeat: 'servers',
panels: [{id: 2, repeat: 'metric'}]
});
dash.templating.list.push({
name: 'servers',
current: { text: 'se1, se2', value: ['se1', 'se2'] },
options: [
{text: 'se1', value: 'se1', selected: true},
{text: 'se2', value: 'se2', selected: true},
]
});
dash.templating.list.push({
name: 'metric',
current: { text: 'm1, m2', value: ['m1', 'm2'] },
options: [
{text: 'm1', value: 'm1', selected: true},
{text: 'm2', value: 'm2', selected: true},
]
});
});
it('should repeat row one time', function() {
expect(ctx.rows.length).to.be(2);
});
it('should repeat panel on both rows', function() {
expect(ctx.rows[0].panels.length).to.be(2);
expect(ctx.rows[1].panels.length).to.be(2);
});
it('should keep panel ids on first row', function() {
expect(ctx.rows[0].panels[0].id).to.be(2);
});
it('should mark second row as repeated', function() {
expect(ctx.rows[0].repeat).to.be('servers');
});
it('should clear repeat field on repeated row', function() {
expect(ctx.rows[1].repeat).to.be(null);
});
it('should generate a repeartRowId based on repeat row index', function() {
expect(ctx.rows[1].repeatRowId).to.be(1);
});
it('should set scopedVars on row panels', function() {
expect(ctx.rows[0].panels[0].scopedVars.servers.value).to.be('se1');
expect(ctx.rows[1].panels[0].scopedVars.servers.value).to.be('se2');
});
});
});

View File

@ -190,6 +190,11 @@ define([
expect(contains).to.be(true); expect(contains).to.be(true);
}); });
it('should not find it if only part matches with $var syntax', function() {
var contains = _templateSrv.containsVariable('this.$ServerDomain.filters', 'Server');
expect(contains).to.be(false);
});
it('should find it with [[var]] syntax', function() { it('should find it with [[var]] syntax', function() {
var contains = _templateSrv.containsVariable('this.[[test]].filters', 'test'); var contains = _templateSrv.containsVariable('this.[[test]].filters', 'test');
expect(contains).to.be(true); expect(contains).to.be(true);