mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge pull request #12991 from pgiraud/repeat_min_span
Repeating panels - Max per row
This commit is contained in:
commit
fda0f92cf9
@ -51,7 +51,7 @@ When a user creates a new dashboard, a new dashboard JSON object is initialized
|
||||
"list": []
|
||||
},
|
||||
"refresh": "5s",
|
||||
"schemaVersion": 16,
|
||||
"schemaVersion": 17,
|
||||
"version": 0,
|
||||
"links": []
|
||||
}
|
||||
|
@ -292,9 +292,11 @@ The `direction` controls how the panels will be arranged.
|
||||
|
||||
By choosing `horizontal` the panels will be arranged side-by-side. Grafana will automatically adjust the width
|
||||
of each repeated panel so that the whole row is filled. Currently, you cannot mix other panels on a row with a repeated
|
||||
panel. Each panel will never be smaller that the provided `Min width` if you have many selected values.
|
||||
panel.
|
||||
|
||||
By choosing `vertical` the panels will be arranged from top to bottom in a column. The `Min width` doesn't have any effect in this case. The width of the repeated panels will be the same as of the first panel (the original template) being repeated.
|
||||
Set `Max per row` to tell grafana how many panels per row you want at most. It defaults to *4* if you don't set anything.
|
||||
|
||||
By choosing `vertical` the panels will be arranged from top to bottom in a column. The width of the repeated panels will be the same as of the first panel (the original template) being repeated.
|
||||
|
||||
Only make changes to the first panel (the original template). To have the changes take effect on all panels you need to trigger a dynamic dashboard re-build.
|
||||
You can do this by either changing the variable value (that is the basis for the repeat) or reload the dashboard.
|
||||
|
@ -112,7 +112,7 @@ func NewDashboard(title string) *Dashboard {
|
||||
func NewDashboardFolder(title string) *Dashboard {
|
||||
folder := NewDashboard(title)
|
||||
folder.IsFolder = true
|
||||
folder.Data.Set("schemaVersion", 16)
|
||||
folder.Data.Set("schemaVersion", 17)
|
||||
folder.Data.Set("version", 0)
|
||||
folder.IsFolder = true
|
||||
return folder
|
||||
|
8
public/app/core/specs/factors.test.ts
Normal file
8
public/app/core/specs/factors.test.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import getFactors from 'app/core/utils/factors';
|
||||
|
||||
describe('factors', () => {
|
||||
it('should return factors for 12', () => {
|
||||
const factors = getFactors(12);
|
||||
expect(factors).toEqual([1, 2, 3, 4, 6, 12]);
|
||||
});
|
||||
});
|
5
public/app/core/utils/factors.ts
Normal file
5
public/app/core/utils/factors.ts
Normal file
@ -0,0 +1,5 @@
|
||||
// Returns the factors of a number
|
||||
// Example getFactors(12) -> [1, 2, 3, 4, 6, 12]
|
||||
export default function getFactors(num: number): number[] {
|
||||
return Array.from(new Array(num + 1), (_, i) => i).filter(i => num % i === 0);
|
||||
}
|
@ -9,6 +9,7 @@ import {
|
||||
} from 'app/core/constants';
|
||||
import { PanelModel } from './panel_model';
|
||||
import { DashboardModel } from './dashboard_model';
|
||||
import getFactors from 'app/core/utils/factors';
|
||||
|
||||
export class DashboardMigrator {
|
||||
dashboard: DashboardModel;
|
||||
@ -21,7 +22,7 @@ export class DashboardMigrator {
|
||||
let i, j, k, n;
|
||||
const oldVersion = this.dashboard.schemaVersion;
|
||||
const panelUpgrades = [];
|
||||
this.dashboard.schemaVersion = 16;
|
||||
this.dashboard.schemaVersion = 17;
|
||||
|
||||
if (oldVersion === this.dashboard.schemaVersion) {
|
||||
return;
|
||||
@ -368,6 +369,24 @@ export class DashboardMigrator {
|
||||
this.upgradeToGridLayout(old);
|
||||
}
|
||||
|
||||
if (oldVersion < 17) {
|
||||
panelUpgrades.push(panel => {
|
||||
if (panel.minSpan) {
|
||||
const max = GRID_COLUMN_COUNT / panel.minSpan;
|
||||
const factors = getFactors(GRID_COLUMN_COUNT);
|
||||
// find the best match compared to factors
|
||||
// (ie. [1,2,3,4,6,12,24] for 24 columns)
|
||||
panel.maxPerRow =
|
||||
factors[
|
||||
_.findIndex(factors, o => {
|
||||
return o > max;
|
||||
}) - 1
|
||||
];
|
||||
}
|
||||
delete panel.minSpan;
|
||||
});
|
||||
}
|
||||
|
||||
if (panelUpgrades.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -442,7 +442,7 @@ export class DashboardModel {
|
||||
}
|
||||
|
||||
const selectedOptions = this.getSelectedVariableOptions(variable);
|
||||
const minWidth = panel.minSpan || 6;
|
||||
const maxPerRow = panel.maxPerRow || 4;
|
||||
let xPos = 0;
|
||||
let yPos = panel.gridPos.y;
|
||||
|
||||
@ -462,7 +462,7 @@ export class DashboardModel {
|
||||
} else {
|
||||
// set width based on how many are selected
|
||||
// assumed the repeated panels should take up full row width
|
||||
copy.gridPos.w = Math.max(GRID_COLUMN_COUNT / selectedOptions.length, minWidth);
|
||||
copy.gridPos.w = Math.max(GRID_COLUMN_COUNT / selectedOptions.length, GRID_COLUMN_COUNT / maxPerRow);
|
||||
copy.gridPos.x = xPos;
|
||||
copy.gridPos.y = yPos;
|
||||
|
||||
|
@ -77,7 +77,7 @@ export class PanelModel {
|
||||
repeatPanelId?: number;
|
||||
repeatDirection?: string;
|
||||
repeatedByRow?: boolean;
|
||||
minSpan?: number;
|
||||
maxPerRow?: number;
|
||||
collapsed?: boolean;
|
||||
panels?: any;
|
||||
soloMode?: boolean;
|
||||
|
@ -127,7 +127,7 @@ describe('DashboardModel', () => {
|
||||
});
|
||||
|
||||
it('dashboard schema version should be set to latest', () => {
|
||||
expect(model.schemaVersion).toBe(16);
|
||||
expect(model.schemaVersion).toBe(17);
|
||||
});
|
||||
|
||||
it('graph thresholds should be migrated', () => {
|
||||
@ -364,14 +364,6 @@ describe('DashboardModel', () => {
|
||||
expect(dashboard.panels.length).toBe(2);
|
||||
});
|
||||
|
||||
it('minSpan should be twice', () => {
|
||||
model.rows = [createRow({ height: 8 }, [[6]])];
|
||||
model.rows[0].panels[0] = { minSpan: 12 };
|
||||
|
||||
const dashboard = new DashboardModel(model);
|
||||
expect(dashboard.panels[0].minSpan).toBe(24);
|
||||
});
|
||||
|
||||
it('should assign id', () => {
|
||||
model.rows = [createRow({ collapse: true, height: 8 }, [[6], [6]])];
|
||||
model.rows[0].panels[0] = {};
|
||||
@ -380,6 +372,16 @@ describe('DashboardModel', () => {
|
||||
expect(dashboard.panels[0].id).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when migrating from minSpan to maxPerRow', () => {
|
||||
it('maxPerRow should be correct', () => {
|
||||
const model = {
|
||||
panels: [{ minSpan: 8 }],
|
||||
};
|
||||
const dashboard = new DashboardModel(model);
|
||||
expect(dashboard.panels[0].maxPerRow).toBe(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function createRow(options, panelDescriptions: any[]) {
|
||||
|
@ -5,6 +5,7 @@ import Remarkable from 'remarkable';
|
||||
import config from 'app/core/config';
|
||||
import { profiler } from 'app/core/core';
|
||||
import { Emitter } from 'app/core/core';
|
||||
import getFactors from 'app/core/utils/factors';
|
||||
import {
|
||||
duplicatePanel,
|
||||
copyPanel as copyPanelUtil,
|
||||
@ -12,7 +13,7 @@ import {
|
||||
sharePanel as sharePanelUtil,
|
||||
} from 'app/features/dashboard/utils/panel';
|
||||
|
||||
import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, PANEL_HEADER_HEIGHT, PANEL_BORDER } from 'app/core/constants';
|
||||
import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT, PANEL_HEADER_HEIGHT, PANEL_BORDER } from 'app/core/constants';
|
||||
|
||||
export class PanelCtrl {
|
||||
panel: any;
|
||||
@ -32,6 +33,7 @@ export class PanelCtrl {
|
||||
events: Emitter;
|
||||
timing: any;
|
||||
loading: boolean;
|
||||
maxPanelsPerRowOptions: number[];
|
||||
|
||||
constructor($scope, $injector) {
|
||||
this.$injector = $injector;
|
||||
@ -92,6 +94,7 @@ export class PanelCtrl {
|
||||
if (!this.editModeInitiated) {
|
||||
this.editModeInitiated = true;
|
||||
this.events.emit('init-edit-mode', null);
|
||||
this.maxPanelsPerRowOptions = getFactors(GRID_COLUMN_COUNT);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,12 +32,17 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="gf-form" ng-show="ctrl.panel.repeat && ctrl.panel.repeatDirection == 'h'">
|
||||
<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,13,14,15,16,17,18,19,20,21,22,23,24]">
|
||||
<span class="gf-form-label width-9">Max per row</span>
|
||||
<select class="gf-form-input" ng-model="ctrl.panel.maxPerRow" ng-options="f for f in [2,3,4,6,12,24]">
|
||||
<option value=""></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="gf-form-hint">
|
||||
<div class="gf-form-hint-text muted">
|
||||
Note: You may need to change the variable selection to see this in action.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -65,7 +65,7 @@
|
||||
}
|
||||
],
|
||||
"rows": [],
|
||||
"schemaVersion": 16,
|
||||
"schemaVersion": 17,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
|
Loading…
Reference in New Issue
Block a user