mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge remote-tracking branch 'upstream/master' into dashboard_permissions
This commit is contained in:
commit
a9e2273064
@ -9,5 +9,6 @@ FROM grafana/docs-base:latest
|
||||
|
||||
COPY config.toml /site
|
||||
COPY awsconfig /site
|
||||
COPY versions.json /site/static/js
|
||||
|
||||
VOLUME ["/site/content"]
|
||||
|
@ -3,6 +3,7 @@ title = "Provisioning"
|
||||
description = ""
|
||||
keywords = ["grafana", "provisioning"]
|
||||
type = "docs"
|
||||
aliases = ["/installation/provisioning"]
|
||||
[menu.docs]
|
||||
parent = "admin"
|
||||
weight = 8
|
||||
@ -66,7 +67,6 @@ Tool | Project
|
||||
-----|------------
|
||||
Puppet | [https://forge.puppet.com/puppet/grafana](https://forge.puppet.com/puppet/grafana)
|
||||
Ansible | [https://github.com/cloudalchemy/ansible-grafana](https://github.com/cloudalchemy/ansible-grafana)
|
||||
Ansible | [https://github.com/picotrading/ansible-grafana](https://github.com/picotrading/ansible-grafana)
|
||||
Chef | [https://github.com/JonathanTron/chef-grafana](https://github.com/JonathanTron/chef-grafana)
|
||||
Saltstack | [https://github.com/salt-formulas/salt-formula-grafana](https://github.com/salt-formulas/salt-formula-grafana)
|
||||
|
||||
|
@ -5,7 +5,7 @@ type = "docs"
|
||||
[menu.docs]
|
||||
name = "Features"
|
||||
identifier = "features"
|
||||
weight = 3
|
||||
weight = 4
|
||||
+++
|
||||
|
||||
|
||||
|
@ -7,6 +7,7 @@ type = "docs"
|
||||
name = "Basic Concepts"
|
||||
identifier = "basic_concepts"
|
||||
parent = "guides"
|
||||
weight = 2
|
||||
+++
|
||||
|
||||
# Basic Concepts
|
||||
|
@ -8,6 +8,7 @@ aliases = ["/guides/gettingstarted"]
|
||||
name = "Getting Started"
|
||||
identifier = "getting_started_guide"
|
||||
parent = "guides"
|
||||
weight = 1
|
||||
+++
|
||||
|
||||
# Getting started
|
||||
@ -24,7 +25,7 @@ Read the [Basic Concepts](/guides/basic_concepts) document to get a crash course
|
||||
|
||||
### Top header
|
||||
|
||||
Let's start with creating a new Dashboard. You can find the new Dashboard link on the right side of the Dashboard picker. You now have a blank Dashboard.
|
||||
Let's start with creating a new Dashboard. You can find the new Dashboard link on the right side of the Dashboard picker. You now have a blank Dashboard.
|
||||
|
||||
<img class="no-shadow" src="/img/docs/v45/top_nav_annotated.png">
|
||||
|
||||
|
@ -4,6 +4,6 @@ type = "docs"
|
||||
[menu.docs]
|
||||
name = "Getting Started"
|
||||
identifier = "guides"
|
||||
weight = 2
|
||||
weight = 3
|
||||
+++
|
||||
|
||||
|
@ -4,10 +4,6 @@ description = "Install guide for Grafana"
|
||||
keywords = ["grafana", "installation", "documentation"]
|
||||
type = "docs"
|
||||
aliases = ["v1.1", "guides/reference/admin"]
|
||||
[menu.docs]
|
||||
name = "Welcome to the Docs"
|
||||
identifier = "root"
|
||||
weight = -1
|
||||
+++
|
||||
|
||||
# Welcome to the Grafana Documentation
|
||||
@ -22,7 +18,7 @@ other domains including industrial sensors, home automation, weather, and proces
|
||||
- [Installing on Mac OS X](installation/mac)
|
||||
- [Installing on Windows](installation/windows)
|
||||
- [Installing on Docker](installation/docker)
|
||||
- [Installing using Provisioning (Chef, Puppet, Salt, Ansible, etc)](installation/provisioning)
|
||||
- [Installing using Provisioning (Chef, Puppet, Salt, Ansible, etc)](administration/provisioning#configuration-management-tools)
|
||||
- [Nightly Builds](https://grafana.com/grafana/download)
|
||||
|
||||
For other platforms Read the [build from source]({{< relref "project/building_from_source.md" >}})
|
||||
|
@ -7,6 +7,7 @@ aliases = ["installation/installation/", "v2.1/installation/install/"]
|
||||
[menu.docs]
|
||||
name = "Installation"
|
||||
identifier = "installation"
|
||||
weight = 1
|
||||
+++
|
||||
|
||||
## Installing Grafana
|
||||
|
@ -3,7 +3,7 @@ title = "What's New in Grafana"
|
||||
[menu.docs]
|
||||
name = "What's New In Grafana"
|
||||
identifier = "whatsnew"
|
||||
weight = 2
|
||||
weight = 3
|
||||
+++
|
||||
|
||||
|
9
docs/versions.json
Normal file
9
docs/versions.json
Normal file
@ -0,0 +1,9 @@
|
||||
[
|
||||
{ "version": "v5.0", "path": "/v5.0", "archived": false },
|
||||
{ "version": "v4.6", "path": "/", "archived": false, "current": true },
|
||||
{ "version": "v4.5", "path": "/v4.5", "archived": true },
|
||||
{ "version": "v4.4", "path": "/v4.4", "archived": true },
|
||||
{ "version": "v4.3", "path": "/v4.3", "archived": true },
|
||||
{ "version": "v4.1", "path": "/v4.1", "archived": true },
|
||||
{ "version": "v3.1", "path": "/v3.1", "archived": true }
|
||||
]
|
@ -71,6 +71,7 @@ func (tw *DatasourcePluginWrapper) Query(ctx context.Context, ds *models.DataSou
|
||||
qr := &tsdb.QueryResult{
|
||||
RefId: r.RefId,
|
||||
Series: []*tsdb.TimeSeries{},
|
||||
Tables: []*tsdb.Table{},
|
||||
}
|
||||
|
||||
if r.Error != "" {
|
||||
|
@ -75,7 +75,7 @@ func TestMappingRowValue(t *testing.T) {
|
||||
boolRowValue, _ := dpw.mapRowValue(&datasource.RowValue{Kind: datasource.RowValue_TYPE_BOOL, BoolValue: true})
|
||||
haveBool, ok := boolRowValue.(bool)
|
||||
if !ok || haveBool != true {
|
||||
t.Fatalf("Expected true, was %s", haveBool)
|
||||
t.Fatalf("Expected true, was %v", haveBool)
|
||||
}
|
||||
|
||||
intRowValue, _ := dpw.mapRowValue(&datasource.RowValue{Kind: datasource.RowValue_TYPE_INT64, Int64Value: 42})
|
||||
|
@ -1,121 +1,121 @@
|
||||
package alerting
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/benbjohnson/clock"
|
||||
)
|
||||
|
||||
func inspectTick(tick time.Time, last time.Time, offset time.Duration, t *testing.T) {
|
||||
if !tick.Equal(last.Add(time.Duration(1) * time.Second)) {
|
||||
t.Fatalf("expected a tick 1 second more than prev, %s. got: %s", last, tick)
|
||||
}
|
||||
}
|
||||
|
||||
// returns the new last tick seen
|
||||
func assertAdvanceUntil(ticker *Ticker, last, desiredLast time.Time, offset, wait time.Duration, t *testing.T) time.Time {
|
||||
for {
|
||||
select {
|
||||
case tick := <-ticker.C:
|
||||
inspectTick(tick, last, offset, t)
|
||||
last = tick
|
||||
case <-time.NewTimer(wait).C:
|
||||
if last.Before(desiredLast) {
|
||||
t.Fatalf("waited %s for ticker to advance to %s, but only went up to %s", wait, desiredLast, last)
|
||||
}
|
||||
if last.After(desiredLast) {
|
||||
t.Fatalf("timer advanced too far. should only have gone up to %s, but it went up to %s", desiredLast, last)
|
||||
}
|
||||
return last
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func assertNoAdvance(ticker *Ticker, desiredLast time.Time, wait time.Duration, t *testing.T) {
|
||||
for {
|
||||
select {
|
||||
case tick := <-ticker.C:
|
||||
t.Fatalf("timer should have stayed at %s, instead it advanced to %s", desiredLast, tick)
|
||||
case <-time.NewTimer(wait).C:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTickerRetro1Hour(t *testing.T) {
|
||||
offset := time.Duration(10) * time.Second
|
||||
last := time.Unix(0, 0)
|
||||
mock := clock.NewMock()
|
||||
mock.Add(time.Duration(1) * time.Hour)
|
||||
desiredLast := mock.Now().Add(-offset)
|
||||
ticker := NewTicker(last, offset, mock)
|
||||
|
||||
last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(10)*time.Millisecond, t)
|
||||
assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)
|
||||
|
||||
}
|
||||
|
||||
func TestAdvanceWithUpdateOffset(t *testing.T) {
|
||||
offset := time.Duration(10) * time.Second
|
||||
last := time.Unix(0, 0)
|
||||
mock := clock.NewMock()
|
||||
mock.Add(time.Duration(1) * time.Hour)
|
||||
desiredLast := mock.Now().Add(-offset)
|
||||
ticker := NewTicker(last, offset, mock)
|
||||
|
||||
last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(10)*time.Millisecond, t)
|
||||
assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)
|
||||
|
||||
// lowering offset should see a few more ticks
|
||||
offset = time.Duration(5) * time.Second
|
||||
ticker.updateOffset(offset)
|
||||
desiredLast = mock.Now().Add(-offset)
|
||||
last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(9)*time.Millisecond, t)
|
||||
assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)
|
||||
|
||||
// advancing clock should see even more ticks
|
||||
mock.Add(time.Duration(1) * time.Hour)
|
||||
desiredLast = mock.Now().Add(-offset)
|
||||
last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(8)*time.Millisecond, t)
|
||||
assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)
|
||||
|
||||
}
|
||||
|
||||
func getCase(lastSeconds, offsetSeconds int) (time.Time, time.Duration) {
|
||||
last := time.Unix(int64(lastSeconds), 0)
|
||||
offset := time.Duration(offsetSeconds) * time.Second
|
||||
return last, offset
|
||||
}
|
||||
|
||||
func TestTickerNoAdvance(t *testing.T) {
|
||||
|
||||
// it's 00:01:00 now. what are some cases where we don't want the ticker to advance?
|
||||
mock := clock.NewMock()
|
||||
mock.Add(time.Duration(60) * time.Second)
|
||||
|
||||
type Case struct {
|
||||
last int
|
||||
offset int
|
||||
}
|
||||
|
||||
// note that some cases add up to now, others go into the future
|
||||
cases := []Case{
|
||||
{50, 10},
|
||||
{50, 30},
|
||||
{59, 1},
|
||||
{59, 10},
|
||||
{59, 30},
|
||||
{60, 1},
|
||||
{60, 10},
|
||||
{60, 30},
|
||||
{90, 1},
|
||||
{90, 10},
|
||||
{90, 30},
|
||||
}
|
||||
for _, c := range cases {
|
||||
last, offset := getCase(c.last, c.offset)
|
||||
ticker := NewTicker(last, offset, mock)
|
||||
assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)
|
||||
}
|
||||
}
|
||||
//import (
|
||||
// "testing"
|
||||
// "time"
|
||||
//
|
||||
// "github.com/benbjohnson/clock"
|
||||
//)
|
||||
//
|
||||
//func inspectTick(tick time.Time, last time.Time, offset time.Duration, t *testing.T) {
|
||||
// if !tick.Equal(last.Add(time.Duration(1) * time.Second)) {
|
||||
// t.Fatalf("expected a tick 1 second more than prev, %s. got: %s", last, tick)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// returns the new last tick seen
|
||||
//func assertAdvanceUntil(ticker *Ticker, last, desiredLast time.Time, offset, wait time.Duration, t *testing.T) time.Time {
|
||||
// for {
|
||||
// select {
|
||||
// case tick := <-ticker.C:
|
||||
// inspectTick(tick, last, offset, t)
|
||||
// last = tick
|
||||
// case <-time.NewTimer(wait).C:
|
||||
// if last.Before(desiredLast) {
|
||||
// t.Fatalf("waited %s for ticker to advance to %s, but only went up to %s", wait, desiredLast, last)
|
||||
// }
|
||||
// if last.After(desiredLast) {
|
||||
// t.Fatalf("timer advanced too far. should only have gone up to %s, but it went up to %s", desiredLast, last)
|
||||
// }
|
||||
// return last
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func assertNoAdvance(ticker *Ticker, desiredLast time.Time, wait time.Duration, t *testing.T) {
|
||||
// for {
|
||||
// select {
|
||||
// case tick := <-ticker.C:
|
||||
// t.Fatalf("timer should have stayed at %s, instead it advanced to %s", desiredLast, tick)
|
||||
// case <-time.NewTimer(wait).C:
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func TestTickerRetro1Hour(t *testing.T) {
|
||||
// offset := time.Duration(10) * time.Second
|
||||
// last := time.Unix(0, 0)
|
||||
// mock := clock.NewMock()
|
||||
// mock.Add(time.Duration(1) * time.Hour)
|
||||
// desiredLast := mock.Now().Add(-offset)
|
||||
// ticker := NewTicker(last, offset, mock)
|
||||
//
|
||||
// last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(10)*time.Millisecond, t)
|
||||
// assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)
|
||||
//
|
||||
//}
|
||||
//
|
||||
//func TestAdvanceWithUpdateOffset(t *testing.T) {
|
||||
// offset := time.Duration(10) * time.Second
|
||||
// last := time.Unix(0, 0)
|
||||
// mock := clock.NewMock()
|
||||
// mock.Add(time.Duration(1) * time.Hour)
|
||||
// desiredLast := mock.Now().Add(-offset)
|
||||
// ticker := NewTicker(last, offset, mock)
|
||||
//
|
||||
// last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(10)*time.Millisecond, t)
|
||||
// assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)
|
||||
//
|
||||
// // lowering offset should see a few more ticks
|
||||
// offset = time.Duration(5) * time.Second
|
||||
// ticker.updateOffset(offset)
|
||||
// desiredLast = mock.Now().Add(-offset)
|
||||
// last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(9)*time.Millisecond, t)
|
||||
// assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)
|
||||
//
|
||||
// // advancing clock should see even more ticks
|
||||
// mock.Add(time.Duration(1) * time.Hour)
|
||||
// desiredLast = mock.Now().Add(-offset)
|
||||
// last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(8)*time.Millisecond, t)
|
||||
// assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)
|
||||
//
|
||||
//}
|
||||
//
|
||||
//func getCase(lastSeconds, offsetSeconds int) (time.Time, time.Duration) {
|
||||
// last := time.Unix(int64(lastSeconds), 0)
|
||||
// offset := time.Duration(offsetSeconds) * time.Second
|
||||
// return last, offset
|
||||
//}
|
||||
//
|
||||
//func TestTickerNoAdvance(t *testing.T) {
|
||||
//
|
||||
// // it's 00:01:00 now. what are some cases where we don't want the ticker to advance?
|
||||
// mock := clock.NewMock()
|
||||
// mock.Add(time.Duration(60) * time.Second)
|
||||
//
|
||||
// type Case struct {
|
||||
// last int
|
||||
// offset int
|
||||
// }
|
||||
//
|
||||
// // note that some cases add up to now, others go into the future
|
||||
// cases := []Case{
|
||||
// {50, 10},
|
||||
// {50, 30},
|
||||
// {59, 1},
|
||||
// {59, 10},
|
||||
// {59, 30},
|
||||
// {60, 1},
|
||||
// {60, 10},
|
||||
// {60, 30},
|
||||
// {90, 1},
|
||||
// {90, 10},
|
||||
// {90, 30},
|
||||
// }
|
||||
// for _, c := range cases {
|
||||
// last, offset := getCase(c.last, c.offset)
|
||||
// ticker := NewTicker(last, offset, mock)
|
||||
// assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)
|
||||
// }
|
||||
//}
|
||||
|
@ -12,7 +12,11 @@ export function toUrlParams(a) {
|
||||
|
||||
let add = function(k, v) {
|
||||
v = typeof v === 'function' ? v() : v === null ? '' : v === undefined ? '' : v;
|
||||
s[s.length] = encodeURIComponent(k) + '=' + encodeURIComponent(v);
|
||||
if (typeof v !== 'boolean') {
|
||||
s[s.length] = encodeURIComponent(k) + '=' + encodeURIComponent(v);
|
||||
} else {
|
||||
s[s.length] = encodeURIComponent(k);
|
||||
}
|
||||
};
|
||||
|
||||
let buildParams = function(prefix, obj) {
|
||||
|
@ -352,16 +352,10 @@ export class DashboardModel {
|
||||
copy.scopedVars[variable.name] = option;
|
||||
|
||||
if (panel.repeatDirection === REPEAT_DIR_VERTICAL) {
|
||||
copy.gridPos.y = yPos;
|
||||
yPos += copy.gridPos.h;
|
||||
|
||||
// Update gridPos for panels below
|
||||
let panelBelowIndex = panelIndex + index + 1;
|
||||
for (let i = panelBelowIndex; i < this.panels.length; i++) {
|
||||
if (this.panels[i].gridPos.y < yPos) {
|
||||
this.panels[i].gridPos.y += copy.gridPos.h;
|
||||
}
|
||||
if (index > 0) {
|
||||
yPos += copy.gridPos.h;
|
||||
}
|
||||
copy.gridPos.y = yPos;
|
||||
} else {
|
||||
// set width based on how many are selected
|
||||
// assumed the repeated panels should take up full row width
|
||||
@ -378,6 +372,15 @@ export class DashboardModel {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update gridPos for panels below
|
||||
let yOffset = yPos - panel.gridPos.y;
|
||||
if (yOffset > 0) {
|
||||
let panelBelowIndex = panelIndex + selectedOptions.length;
|
||||
for (let i = panelBelowIndex; i < this.panels.length; i++) {
|
||||
this.panels[i].gridPos.y += yOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repeatRow(panel: PanelModel, panelIndex: number, variable) {
|
||||
@ -566,6 +569,7 @@ export class DashboardModel {
|
||||
|
||||
if (row.collapsed) {
|
||||
row.collapsed = false;
|
||||
let hasRepeat = false;
|
||||
|
||||
if (row.panels.length > 0) {
|
||||
// Use first panel to figure out if it was moved or pushed
|
||||
@ -586,6 +590,10 @@ export class DashboardModel {
|
||||
// update insert post and y max
|
||||
insertPos += 1;
|
||||
yMax = Math.max(yMax, panel.gridPos.y + panel.gridPos.h);
|
||||
|
||||
if (panel.repeat) {
|
||||
hasRepeat = true;
|
||||
}
|
||||
}
|
||||
|
||||
const pushDownAmount = yMax - row.gridPos.y;
|
||||
@ -596,6 +604,10 @@ export class DashboardModel {
|
||||
}
|
||||
|
||||
row.panels = [];
|
||||
|
||||
if (hasRepeat) {
|
||||
this.processRepeats();
|
||||
}
|
||||
}
|
||||
|
||||
// sort panels
|
||||
|
@ -4,6 +4,57 @@ import { expect } from 'test/lib/common';
|
||||
|
||||
jest.mock('app/core/services/context_srv', () => ({}));
|
||||
|
||||
describe('given dashboard with panel repeat', function() {
|
||||
var dashboard;
|
||||
|
||||
beforeEach(function() {
|
||||
let dashboardJSON = {
|
||||
panels: [
|
||||
{ id: 1, type: 'row', gridPos: { x: 0, y: 0, h: 1, w: 24 } },
|
||||
{ id: 2, repeat: 'apps', repeatDirection: 'h', gridPos: { x: 0, y: 1, h: 2, w: 8 } },
|
||||
],
|
||||
templating: {
|
||||
list: [
|
||||
{
|
||||
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 },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
dashboard = new DashboardModel(dashboardJSON);
|
||||
dashboard.processRepeats();
|
||||
});
|
||||
|
||||
it('should repeat panels when row is expanding', function() {
|
||||
expect(dashboard.panels.length).toBe(4);
|
||||
|
||||
// toggle row
|
||||
dashboard.toggleRow(dashboard.panels[0]);
|
||||
expect(dashboard.panels.length).toBe(1);
|
||||
|
||||
// change variable
|
||||
dashboard.templating.list[0].options[2].selected = false;
|
||||
dashboard.templating.list[0].current = {
|
||||
text: 'se1, se2',
|
||||
value: ['se1', 'se2'],
|
||||
};
|
||||
|
||||
// toggle row back
|
||||
dashboard.toggleRow(dashboard.panels[0]);
|
||||
expect(dashboard.panels.length).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('given dashboard with panel repeat in horizontal direction', function() {
|
||||
var dashboard;
|
||||
|
||||
@ -178,6 +229,88 @@ describe('given dashboard with panel repeat in vertical direction', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('given dashboard with row repeat and panel repeat in horizontal direction', () => {
|
||||
let dashboard, dashboardJSON;
|
||||
|
||||
beforeEach(() => {
|
||||
dashboardJSON = {
|
||||
panels: [
|
||||
{ id: 1, type: 'row', repeat: 'region', gridPos: { x: 0, y: 0, h: 1, w: 24 } },
|
||||
{ id: 2, type: 'graph', repeat: 'app', gridPos: { x: 0, y: 1, h: 2, w: 6 } },
|
||||
],
|
||||
templating: {
|
||||
list: [
|
||||
{
|
||||
name: 'region',
|
||||
current: {
|
||||
text: 'reg1, reg2',
|
||||
value: ['reg1', 'reg2'],
|
||||
},
|
||||
options: [{ text: 'reg1', value: 'reg1', selected: true }, { text: 'reg2', value: 'reg2', selected: true }],
|
||||
},
|
||||
{
|
||||
name: 'app',
|
||||
current: {
|
||||
text: 'se1, se2, se3, se4, se5, se6',
|
||||
value: ['se1', 'se2', 'se3', 'se4', 'se5', 'se6'],
|
||||
},
|
||||
options: [
|
||||
{ text: 'se1', value: 'se1', selected: true },
|
||||
{ text: 'se2', value: 'se2', selected: true },
|
||||
{ text: 'se3', value: 'se3', selected: true },
|
||||
{ text: 'se4', value: 'se4', selected: true },
|
||||
{ text: 'se5', value: 'se5', selected: true },
|
||||
{ text: 'se6', value: 'se6', selected: true },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
dashboard = new DashboardModel(dashboardJSON);
|
||||
dashboard.processRepeats(false);
|
||||
});
|
||||
|
||||
it('should panels in self row', () => {
|
||||
const panel_types = _.map(dashboard.panels, 'type');
|
||||
expect(panel_types).toEqual([
|
||||
'row',
|
||||
'graph',
|
||||
'graph',
|
||||
'graph',
|
||||
'graph',
|
||||
'graph',
|
||||
'graph',
|
||||
'row',
|
||||
'graph',
|
||||
'graph',
|
||||
'graph',
|
||||
'graph',
|
||||
'graph',
|
||||
'graph',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should be placed in their places', function() {
|
||||
expect(dashboard.panels[0].gridPos).toMatchObject({ x: 0, y: 0, h: 1, w: 24 }); // 1st row
|
||||
|
||||
expect(dashboard.panels[1].gridPos).toMatchObject({ x: 0, y: 1, h: 2, w: 6 });
|
||||
expect(dashboard.panels[2].gridPos).toMatchObject({ x: 6, y: 1, h: 2, w: 6 });
|
||||
expect(dashboard.panels[3].gridPos).toMatchObject({ x: 12, y: 1, h: 2, w: 6 });
|
||||
expect(dashboard.panels[4].gridPos).toMatchObject({ x: 18, y: 1, h: 2, w: 6 });
|
||||
expect(dashboard.panels[5].gridPos).toMatchObject({ x: 0, y: 3, h: 2, w: 6 }); // next row
|
||||
expect(dashboard.panels[6].gridPos).toMatchObject({ x: 6, y: 3, h: 2, w: 6 });
|
||||
|
||||
expect(dashboard.panels[7].gridPos).toMatchObject({ x: 0, y: 5, h: 1, w: 24 });
|
||||
|
||||
expect(dashboard.panels[8].gridPos).toMatchObject({ x: 0, y: 6, h: 2, w: 6 }); // 2nd row
|
||||
expect(dashboard.panels[9].gridPos).toMatchObject({ x: 6, y: 6, h: 2, w: 6 });
|
||||
expect(dashboard.panels[10].gridPos).toMatchObject({ x: 12, y: 6, h: 2, w: 6 });
|
||||
expect(dashboard.panels[11].gridPos).toMatchObject({ x: 18, y: 6, h: 2, w: 6 }); // next row
|
||||
expect(dashboard.panels[12].gridPos).toMatchObject({ x: 0, y: 8, h: 2, w: 6 });
|
||||
expect(dashboard.panels[13].gridPos).toMatchObject({ x: 6, y: 8, h: 2, w: 6 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('given dashboard with row repeat', function() {
|
||||
let dashboard, dashboardJSON;
|
||||
|
||||
|
@ -12,6 +12,7 @@ export class PlaylistSearchCtrl {
|
||||
|
||||
$timeout(() => {
|
||||
this.query.query = '';
|
||||
this.query.type = 'dash-db';
|
||||
this.searchDashboards();
|
||||
}, 100);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-7">Metric</label>
|
||||
<label class="gf-form-label query-keyword width-8">Metric</label>
|
||||
|
||||
<metric-segment segment="regionSegment" get-options="getRegions()" on-change="regionChanged()"></metric-segment>
|
||||
<metric-segment segment="namespaceSegment" get-options="getNamespaces()" on-change="namespaceChanged()"></metric-segment>
|
||||
@ -22,7 +22,7 @@
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-7">Dimensions</label>
|
||||
<label class="gf-form-label query-keyword width-8">Dimensions</label>
|
||||
<metric-segment ng-repeat="segment in dimSegments" segment="segment" get-options="getDimSegments(segment, $index)" on-change="dimSegmentChanged(segment, $index)"></metric-segment>
|
||||
</div>
|
||||
|
||||
@ -33,9 +33,9 @@
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-7">
|
||||
Period
|
||||
<info-popover mode="right-normal">Interval between points in seconds</info-popover>
|
||||
<label class="gf-form-label query-keyword width-8">
|
||||
Min period
|
||||
<info-popover mode="right-normal">Minimum interval between points in seconds</info-popover>
|
||||
</label>
|
||||
<input type="text" class="gf-form-input" ng-model="target.period" spellcheck='false' placeholder="auto" ng-model-onblur ng-change="onChange()" />
|
||||
</div>
|
||||
|
@ -1,15 +1,15 @@
|
||||
<div class="panel-alert-list">
|
||||
<div class="panel-alert-list__no-alerts" ng-show="ctrl.noAlertsMessage">
|
||||
{{ctrl.noAlertsMessage}}
|
||||
</div>
|
||||
<div class="panel-alert-list__no-alerts" ng-show="ctrl.noAlertsMessage">
|
||||
{{ctrl.noAlertsMessage}}
|
||||
</div>
|
||||
|
||||
<section ng-if="ctrl.panel.show === 'current'">
|
||||
<ol class="alert-rule-list">
|
||||
<li class="alert-rule-item" ng-repeat="alert in ctrl.currentAlerts">
|
||||
<div class="alert-rule-item__icon {{alert.stateModel.stateClass}}">
|
||||
<i class="{{alert.stateModel.iconClass}}"></i>
|
||||
</div>
|
||||
<div class="alert-rule-item__body">
|
||||
<div class="alert-rule-item__icon {{alert.stateModel.stateClass}}">
|
||||
<i class="{{alert.stateModel.iconClass}}"></i>
|
||||
</div>
|
||||
<div class="alert-rule-item__header">
|
||||
<p class="alert-rule-item__name">
|
||||
<a href="dashboard/{{alert.dashboardUri}}?panelId={{alert.panelId}}&fullscreen&edit&tab=alert">
|
||||
|
@ -24,4 +24,10 @@ describe('ViewStore', () => {
|
||||
expect(toJS(store.query.get('values'))).toMatchObject(['A', 'B']);
|
||||
expect(store.currentUrl).toBe('/hello?values=A&values=B');
|
||||
});
|
||||
|
||||
it('Query can contain boolean', () => {
|
||||
store.updatePathAndQuery('/hello', { abool: true });
|
||||
expect(toJS(store.query.get('abool'))).toBe(true);
|
||||
expect(store.currentUrl).toBe('/hello?abool');
|
||||
});
|
||||
});
|
||||
|
@ -135,6 +135,7 @@ $list-item-bg: $card-background;
|
||||
$list-item-hover-bg: lighten($gray-blue, 2%);
|
||||
$list-item-link-color: $text-color;
|
||||
$list-item-shadow: $card-shadow;
|
||||
$empty-list-cta-bg: $gray-blue;
|
||||
|
||||
// Scrollbars
|
||||
$scrollbarBackground: #404357;
|
||||
|
@ -133,6 +133,7 @@ $list-item-bg: linear-gradient(135deg, $gray-5, $gray-6); //$card-background;
|
||||
$list-item-hover-bg: darken($gray-5, 5%);
|
||||
$list-item-link-color: $text-color;
|
||||
$list-item-shadow: $card-shadow;
|
||||
$empty-list-cta-bg: $gray-6;
|
||||
|
||||
// Tables
|
||||
// -------------------------
|
||||
|
@ -1,5 +1,5 @@
|
||||
.empty-list-cta {
|
||||
background-color: $search-filter-box-bg;
|
||||
background-color: $empty-list-cta-bg;
|
||||
text-align: center;
|
||||
padding: $spacer*2;
|
||||
border-radius: $border-radius;
|
||||
|
Loading…
Reference in New Issue
Block a user