Commit e5a6cb62 by Torkel Ödegaard

newgrid: added constants, changed grid to 24 cols, added tests for panel repeats

parent 9fb4f619
export const GRID_CELL_HEIGHT = 20;
export const GRID_CELL_VMARGIN = 10;
export const GRID_COLUMN_COUNT = 24;
import React from 'react'; import React from 'react';
import coreModule from 'app/core/core_module'; import coreModule from 'app/core/core_module';
import ReactGridLayout from 'react-grid-layout'; import ReactGridLayout from 'react-grid-layout';
import {CELL_HEIGHT, CELL_VMARGIN} from '../dashboard_model'; import {GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT} from 'app/core/constants';
import {DashboardPanel} from './DashboardPanel'; import {DashboardPanel} from './DashboardPanel';
import {DashboardModel} from '../dashboard_model'; import {DashboardModel} from '../dashboard_model';
import {PanelContainer} from './PanelContainer'; import {PanelContainer} from './PanelContainer';
...@@ -9,7 +9,6 @@ import {PanelModel} from '../panel_model'; ...@@ -9,7 +9,6 @@ import {PanelModel} from '../panel_model';
import classNames from 'classnames'; import classNames from 'classnames';
import sizeMe from 'react-sizeme'; import sizeMe from 'react-sizeme';
const COLUMN_COUNT = 12;
let lastGridWidth = 1200; let lastGridWidth = 1200;
function GridWrapper({size, layout, onLayoutChange, children, onResize, onResizeStop, onWidthChange}) { function GridWrapper({size, layout, onLayoutChange, children, onResize, onResizeStop, onWidthChange}) {
...@@ -32,9 +31,9 @@ function GridWrapper({size, layout, onLayoutChange, children, onResize, onResize ...@@ -32,9 +31,9 @@ function GridWrapper({size, layout, onLayoutChange, children, onResize, onResize
measureBeforeMount={false} measureBeforeMount={false}
containerPadding={[0, 0]} containerPadding={[0, 0]}
useCSSTransforms={false} useCSSTransforms={false}
margin={[CELL_VMARGIN, CELL_VMARGIN]} margin={[GRID_CELL_VMARGIN, GRID_CELL_VMARGIN]}
cols={COLUMN_COUNT} cols={GRID_COLUMN_COUNT}
rowHeight={CELL_HEIGHT} rowHeight={GRID_CELL_HEIGHT}
draggableHandle=".grid-drag-handle" draggableHandle=".grid-drag-handle"
layout={layout} layout={layout}
onResize={onResize} onResize={onResize}
......
///<reference path="../../../headers/common.d.ts" />
import {coreModule} from 'app/core/core'; import {coreModule} from 'app/core/core';
var template = ` var template = `
<div class="gf-form-select-wrapper max-width-13"> <div class="gf-form-select-wrapper max-width-13">
<select class="gf-form-input" ng-model="model.repeat" ng-options="f.value as f.text for f in variables"> <select class="gf-form-input" ng-model="model.repeat" ng-options="f.value as f.text for f in variables" ng-change="optionChanged()">
<option value=""></option> <option value=""></option>
</div> </div>
`; `;
...@@ -29,6 +27,17 @@ function dashRepeatOptionDirective(variableSrv) { ...@@ -29,6 +27,17 @@ function dashRepeatOptionDirective(variableSrv) {
} }
scope.variables.unshift({text: 'Disabled', value: null}); scope.variables.unshift({text: 'Disabled', value: null});
// if repeat is set and no direction set to horizontal
if (scope.panel.repeat && !scope.panel.repeatDirection) {
scope.panel.repeatDirection = 'h';
}
scope.optionChanged = function() {
if (scope.panel.repeat) {
scope.panel.repeatDirection = 'h';
}
};
} }
}; };
} }
......
...@@ -400,20 +400,126 @@ describe('DashboardModel', function() { ...@@ -400,20 +400,126 @@ describe('DashboardModel', function() {
}); });
describe('updateSubmenuVisibility with hidden annotation toggle', function() { describe('updateSubmenuVisibility with hidden annotation toggle', function() {
var model; var dashboard;
beforeEach(function() { beforeEach(function() {
model = new DashboardModel({ dashboard = new DashboardModel({
annotations: { annotations: {
list: [{hide: true}] list: [{hide: true}]
} }
}); });
model.updateSubmenuVisibility(); dashboard.updateSubmenuVisibility();
}); });
it('should not enable submmenu', function() { it('should not enable submmenu', function() {
expect(model.meta.submenuEnabled).to.be(false); expect(dashboard.meta.submenuEnabled).to.be(false);
}); });
}); });
describe('given dashboard with panel repeat', function(ctx) {
var dashboard;
beforeEach(function() {
dashboard = new DashboardModel({
panels: [{id: 2, repeat: 'apps'}],
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.processRepeats();
});
it('should repeat panel 3 times', function() {
expect(dashboard.panels.length).to.be(3);
});
it('should mark panel repeated', function() {
expect(dashboard.panels[0].repeat).to.be('apps');
expect(dashboard.panels[1].repeatPanelId).to.be(2);
});
it('should set scopedVars on panels', function() {
expect(dashboard.panels[0].scopedVars.apps.value).to.be('se1');
expect(dashboard.panels[1].scopedVars.apps.value).to.be('se2');
expect(dashboard.panels[2].scopedVars.apps.value).to.be('se3');
});
describe('After a second iteration', function() {
var repeatedPanelAfterIteration1;
beforeEach(function() {
repeatedPanelAfterIteration1 = dashboard.panels[1];
dashboard.panels[0].fill = 10;
dashboard.processRepeats();
});
it('reused panel should copy properties from source', function() {
expect(dashboard.panels[1].fill).to.be(10);
});
it('should have same panel count', function() {
expect(dashboard.panels.length).to.be(3);
});
});
describe('After a second iteration with different variable', function() {
beforeEach(function() {
dashboard.templating.list.push({
name: 'server',
current: { text: 'se1, se2, se3', value: ['se1']},
options: [{text: 'se1', value: 'se1', selected: true}]
});
dashboard.panels[0].repeat = "server";
dashboard.processRepeats();
});
it('should remove scopedVars value for last variable', function() {
expect(dashboard.panels[0].scopedVars.apps).to.be(undefined);
});
it('should have new variable value in scopedVars', function() {
expect(dashboard.panels[0].scopedVars.server.value).to.be("se1");
});
});
describe('After a second iteration and selected values reduced', function() {
beforeEach(function() {
dashboard.templating.list[0].options[1].selected = false;
dashboard.processRepeats();
});
it('should clean up repeated panel', function() {
expect(dashboard.panels.length).to.be(2);
});
});
describe('After a second iteration and panel repeat is turned off', function() {
beforeEach(function() {
dashboard.panels[0].repeat = null;
dashboard.processRepeats();
});
it('should clean up repeated panel', function() {
expect(dashboard.panels.length).to.be(1);
});
it('should remove scoped vars from reused panel', function() {
expect(dashboard.panels[0].scopedVars).to.be(undefined);
});
});
});
}); });
// import {describe, beforeEach, it, expect, angularMocks} from 'test/lib/common';
//
// import '../dashboard_srv';
// import {DynamicDashboardSrv} from '../dynamic_dashboard_srv';
//
// function dynamicDashScenario(desc, func) {
//
// describe.skip(desc, function() {
// var ctx: any = {};
//
// ctx.setup = function (setupFunc) {
//
// beforeEach(angularMocks.module('grafana.core'));
// beforeEach(angularMocks.module('grafana.services'));
// beforeEach(angularMocks.module(function($provide) {
// $provide.value('contextSrv', {
// user: { timezone: 'utc'}
// });
// }));
//
// beforeEach(angularMocks.inject(function(dashboardSrv) {
// ctx.dashboardSrv = dashboardSrv;
//
// var model = {
// rows: [],
// templating: { list: [] }
// };
//
// setupFunc(model);
// ctx.dash = ctx.dashboardSrv.create(model);
// ctx.dynamicDashboardSrv = new DynamicDashboardSrv();
// ctx.dynamicDashboardSrv.init(ctx.dash);
// ctx.dynamicDashboardSrv.process();
// 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.process();
// });
//
// 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 with different variable', function() {
// beforeEach(function() {
// ctx.dash.templating.list.push({
// name: 'server',
// current: { text: 'se1, se2, se3', value: ['se1']},
// options: [{text: 'se1', value: 'se1', selected: true}]
// });
// ctx.rows[0].panels[0].repeat = "server";
// ctx.dynamicDashboardSrv.process();
// });
//
// it('should remove scopedVars value for last variable', function() {
// expect(ctx.rows[0].panels[0].scopedVars.apps).to.be(undefined);
// });
//
// it('should have new variable value in scopedVars', function() {
// expect(ctx.rows[0].panels[0].scopedVars.server.value).to.be("se1");
// });
// });
//
// describe('After a second iteration and selected values reduced', function() {
// beforeEach(function() {
// ctx.dash.templating.list[0].options[1].selected = false;
// ctx.dynamicDashboardSrv.process();
// });
//
// 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.process();
// });
//
// 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(undefined);
// });
// });
//
// });
//
// 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.process();
// });
//
// 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.process();
// });
//
// 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');
// });
//
// });
...@@ -3,7 +3,7 @@ import _ from 'lodash'; ...@@ -3,7 +3,7 @@ import _ from 'lodash';
import $ from 'jquery'; import $ from 'jquery';
import {appEvents, profiler} from 'app/core/core'; import {appEvents, profiler} from 'app/core/core';
import Remarkable from 'remarkable'; import Remarkable from 'remarkable';
import {CELL_HEIGHT, CELL_VMARGIN} from '../dashboard/dashboard_model'; import {GRID_CELL_HEIGHT, GRID_CELL_VMARGIN} from 'app/core/constants';
const TITLE_HEIGHT = 25; const TITLE_HEIGHT = 25;
const EMPTY_TITLE_HEIGHT = 9; const EMPTY_TITLE_HEIGHT = 9;
...@@ -163,7 +163,7 @@ export class PanelCtrl { ...@@ -163,7 +163,7 @@ export class PanelCtrl {
var fullscreenHeight = Math.floor(docHeight * 0.8); var fullscreenHeight = Math.floor(docHeight * 0.8);
this.containerHeight = this.editMode ? editHeight : fullscreenHeight; this.containerHeight = this.editMode ? editHeight : fullscreenHeight;
} else { } else {
this.containerHeight = this.panel.gridPos.h * CELL_HEIGHT + ((this.panel.gridPos.h-1) * CELL_VMARGIN); this.containerHeight = this.panel.gridPos.h * GRID_CELL_HEIGHT + ((this.panel.gridPos.h-1) * GRID_CELL_VMARGIN);
} }
this.height = this.containerHeight - (PANEL_BORDER + PANEL_PADDING + (this.panel.title ? TITLE_HEIGHT : EMPTY_TITLE_HEIGHT)); this.height = this.containerHeight - (PANEL_BORDER + PANEL_PADDING + (this.panel.title ? TITLE_HEIGHT : EMPTY_TITLE_HEIGHT));
......
...@@ -9,24 +9,24 @@ ...@@ -9,24 +9,24 @@
<span class="gf-form-label width-7">Description</span> <span class="gf-form-label width-7">Description</span>
<textarea class="gf-form-input width-25" rows="3" ng-model="ctrl.panel.description" placeholder="Panel description, supports markdown & links"></textarea> <textarea class="gf-form-input width-25" rows="3" ng-model="ctrl.panel.description" placeholder="Panel description, supports markdown & links"></textarea>
</div> </div>
<gf-form-switch class="gf-form" label-class="width-7" switch-class="max-width-6" label="Transparent" checked="ctrl.panel.transparent" on-change="ctrl.render()"></gf-form-switch>
</div> </div>
<div class="section gf-form-group"> <div class="section gf-form-group">
<h5 class="section-heading">Options</h5> <h5 class="section-heading">Repeat</h5>
<gf-form-switch class="gf-form" label-class="width-8" switch-class="max-width-6" label="Transparent" checked="ctrl.panel.transparent" on-change="ctrl.render()"></gf-form-switch>
<div class="gf-form"> <div class="gf-form">
<span class="gf-form-label width-8">Repeat Panel</span> <span class="gf-form-label width-9">For each value of</span>
<dash-repeat-option model="ctrl.panel"></dash-repeat-option> <dash-repeat-option model="ctrl.panel"></dash-repeat-option>
</div> </div>
<div class="gf-form"> <div class="gf-form" ng-show="ctrl.panel.repeat">
<span class="gf-form-label width-8">Min width</span> <span class="gf-form-label width-9">Min width</span>
<select class="gf-form-input" ng-model="ctrl.panel.minSpan" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10,11,12]"> <select class="gf-form-input" ng-model="ctrl.panel.minSpan" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10,11,12]">
<option value=""></option> <option value=""></option>
</select> </select>
</div> </div>
<div class="gf-form"> <div class="gf-form" ng-show="ctrl.panel.repeat">
<span class="gf-form-label width-8">Direction</span> <span class="gf-form-label width-9">Direction</span>
<select class="gf-form-input" ng-model="ctrl.panel.repeatDirection" ng-options="f for f in ['X', 'Y']"> <select class="gf-form-input" ng-model="ctrl.panel.repeatDirection" ng-options="f.value as f.text for f in [{value: 'v', text: 'Vertical'}, {value: 'h', text: 'Horizontal'}]">
<option value=""></option> <option value=""></option>
</select> </select>
</div> </div>
......
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
"transparent": true, "transparent": true,
"type": "text", "type": "text",
"gridPos": { "gridPos": {
"w": 12, "w": 24,
"h": 2, "h": 3,
"x": 0, "x": 0,
"y": 0 "y": 0
} }
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
"transparent": false, "transparent": false,
"type": "dashlist", "type": "dashlist",
"gridPos": { "gridPos": {
"w": 7, "w": 12,
"h": 17, "h": 17,
"x": 0, "x": 0,
"y": 6 "y": 6
...@@ -57,9 +57,9 @@ ...@@ -57,9 +57,9 @@
"transparent": false, "transparent": false,
"type": "pluginlist", "type": "pluginlist",
"gridPos": { "gridPos": {
"w": 5, "w": 12,
"h": 17, "h": 17,
"x": 7, "x": 12,
"y": 6 "y": 6
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment