Commit 37f9bdfc by Patrick O'Carroll Committed by Marcus Efraimsson

save modal ux improvements (#11822)

changes to save modal when saving an updated dashboard

Changed time range and variables are now not saved by default, 
you'll need to actively choose if you want to save updated time 
range and or variables.
parent 938deae4
......@@ -22,8 +22,10 @@ export class DashboardModel {
editable: any;
graphTooltip: any;
time: any;
originalTime: any;
timepicker: any;
templating: any;
originalTemplating: any;
annotations: any;
refresh: any;
snapshot: any;
......@@ -68,8 +70,12 @@ export class DashboardModel {
this.editable = data.editable !== false;
this.graphTooltip = data.graphTooltip || 0;
this.time = data.time || { from: 'now-6h', to: 'now' };
this.originalTime = _.cloneDeep(this.time);
this.timepicker = data.timepicker || {};
this.templating = this.ensureListExist(data.templating);
this.originalTemplating = _.map(this.templating.list, variable => {
return { name: variable.name, current: _.clone(variable.current) };
});
this.annotations = this.ensureListExist(data.annotations);
this.refresh = data.refresh;
this.snapshot = data.snapshot;
......@@ -130,7 +136,12 @@ export class DashboardModel {
}
// cleans meta data and other non persistent state
getSaveModelClone() {
getSaveModelClone(options?) {
let defaults = _.defaults(options || {}, {
saveVariables: false,
saveTimerange: false,
});
// make clone
var copy: any = {};
for (var property in this) {
......@@ -142,10 +153,23 @@ export class DashboardModel {
}
// get variable save models
//console.log(this.templating.list);
copy.templating = {
list: _.map(this.templating.list, variable => (variable.getSaveModel ? variable.getSaveModel() : variable)),
};
if (!defaults.saveVariables && copy.templating.list.length === this.originalTemplating.length) {
for (let i = 0; i < copy.templating.list.length; i++) {
if (copy.templating.list[i].name === this.originalTemplating[i].name) {
copy.templating.list[i].current = this.originalTemplating[i].current;
}
}
}
if (!defaults.saveTimerange) {
copy.time = this.originalTime;
}
// get panel save models
copy.panels = _.chain(this.panels)
.filter(panel => panel.type !== 'add-panel')
......
import coreModule from 'app/core/core_module';
import _ from 'lodash';
const template = `
<div class="modal-body">
......@@ -14,19 +15,29 @@ const template = `
</div>
<form name="ctrl.saveForm" ng-submit="ctrl.save()" class="modal-content" novalidate>
<h6 class="text-center">Add a note to describe your changes</h6>
<div class="p-t-2">
<div class="p-t-1">
<div class="gf-form-group" ng-if="ctrl.timeChange || ctrl.variableChange">
<gf-form-switch class="gf-form"
label="Save current time range" ng-if="ctrl.timeChange" label-class="width-12" switch-class="max-width-6"
checked="ctrl.saveTimerange" on-change="buildUrl()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label="Save current variables" ng-if="ctrl.variableChange" label-class="width-12" switch-class="max-width-6"
checked="ctrl.saveVariables" on-change="buildUrl()">
</gf-form-switch>
</div>
<div class="gf-form">
<label class="gf-form-hint">
<input
type="text"
name="message"
class="gf-form-input"
placeholder="Updates to &hellip;"
placeholder="Add a note to describe your changes &hellip;"
give-focus="true"
ng-model="ctrl.message"
ng-model-options="{allowInvalid: true}"
ng-maxlength="this.max"
maxlength="64"
autocomplete="off" />
<small class="gf-form-hint-text muted" ng-cloak>
<span ng-class="{'text-error': ctrl.saveForm.message.$invalid && ctrl.saveForm.message.$dirty }">
......@@ -40,7 +51,7 @@ const template = `
<div class="gf-form-button-row text-center">
<button type="submit" class="btn btn-success" ng-disabled="ctrl.saveForm.$invalid">Save</button>
<button class="btn btn-inverse" ng-click="ctrl.dismiss();">Cancel</button>
<a class="btn btn-link" ng-click="ctrl.dismiss();">Cancel</a>
</div>
</form>
</div>
......@@ -48,14 +59,51 @@ const template = `
export class SaveDashboardModalCtrl {
message: string;
saveVariables = false;
saveTimerange = false;
templating: any;
time: any;
originalTime: any;
current = [];
originalCurrent = [];
max: number;
saveForm: any;
dismiss: () => void;
timeChange = false;
variableChange = false;
/** @ngInject */
constructor(private dashboardSrv) {
this.message = '';
this.max = 64;
this.templating = dashboardSrv.dash.templating.list;
this.compareTemplating();
this.compareTime();
}
compareTime() {
if (_.isEqual(this.dashboardSrv.dash.time, this.dashboardSrv.dash.originalTime)) {
this.timeChange = false;
} else {
this.timeChange = true;
}
}
compareTemplating() {
if (this.dashboardSrv.dash.templating.list.length > 0) {
for (let i = 0; i < this.dashboardSrv.dash.templating.list.length; i++) {
if (
this.dashboardSrv.dash.templating.list[i].current.text !==
this.dashboardSrv.dash.originalTemplating[i].current.text
) {
return (this.variableChange = true);
}
}
return (this.variableChange = false);
} else {
return (this.variableChange = false);
}
}
save() {
......@@ -63,9 +111,14 @@ export class SaveDashboardModalCtrl {
return;
}
var options = {
saveVariables: this.saveVariables,
saveTimerange: this.saveTimerange,
message: this.message,
};
var dashboard = this.dashboardSrv.getCurrent();
var saveModel = dashboard.getSaveModelClone();
var options = { message: this.message };
var saveModel = dashboard.getSaveModelClone(options);
return this.dashboardSrv.save(saveModel, options).then(this.dismiss);
}
......
......@@ -434,4 +434,63 @@ describe('DashboardModel', function() {
});
});
});
describe('save variables and timeline', () => {
let model;
beforeEach(() => {
model = new DashboardModel({
templating: {
list: [
{
name: 'Server',
current: {
selected: true,
text: 'server_001',
value: 'server_001',
},
},
],
},
time: {
from: 'now-6h',
to: 'now',
},
});
model.templating.list[0] = {
name: 'Server',
current: {
selected: true,
text: 'server_002',
value: 'server_002',
},
};
model.time = {
from: 'now-3h',
to: 'now',
};
});
it('should not save variables and timeline', () => {
let options = {
saveVariables: false,
saveTimerange: false,
};
let saveModel = model.getSaveModelClone(options);
expect(saveModel.templating.list[0].current.text).toBe('server_001');
expect(saveModel.time.from).toBe('now-6h');
});
it('should save variables and timeline', () => {
let options = {
saveVariables: true,
saveTimerange: true,
};
let saveModel = model.getSaveModelClone(options);
expect(saveModel.templating.list[0].current.text).toBe('server_002');
expect(saveModel.time.from).toBe('now-3h');
});
});
});
import { SaveDashboardModalCtrl } from '../save_modal';
jest.mock('app/core/services/context_srv', () => ({}));
describe('SaveDashboardModal', () => {
describe('save modal checkboxes', () => {
it('should show checkboxes', () => {
let fakeDashboardSrv = {
dash: {
templating: {
list: [
{
current: {
selected: true,
tags: Array(0),
text: 'server_001',
value: 'server_001',
},
name: 'Server',
},
],
},
originalTemplating: [
{
current: {
selected: true,
text: 'server_002',
value: 'server_002',
},
name: 'Server',
},
],
time: {
from: 'now-3h',
to: 'now',
},
originalTime: {
from: 'now-6h',
to: 'now',
},
},
};
let modal = new SaveDashboardModalCtrl(fakeDashboardSrv);
expect(modal.timeChange).toBe(true);
expect(modal.variableChange).toBe(true);
});
it('should hide checkboxes', () => {
let fakeDashboardSrv = {
dash: {
templating: {
list: [
{
current: {
selected: true,
//tags: Array(0),
text: 'server_002',
value: 'server_002',
},
name: 'Server',
},
],
},
originalTemplating: [
{
current: {
selected: true,
text: 'server_002',
value: 'server_002',
},
name: 'Server',
},
],
time: {
from: 'now-3h',
to: 'now',
},
originalTime: {
from: 'now-3h',
to: 'now',
},
},
};
let modal = new SaveDashboardModalCtrl(fakeDashboardSrv);
expect(modal.timeChange).toBe(false);
expect(modal.variableChange).toBe(false);
});
});
});
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