Просмотр исходного кода

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.
Patrick O'Carroll 7 лет назад
Родитель
Сommit
37f9bdfc8c

+ 25 - 1
public/app/features/dashboard/dashboard_model.ts

@@ -22,8 +22,10 @@ export class DashboardModel {
   editable: any;
   editable: any;
   graphTooltip: any;
   graphTooltip: any;
   time: any;
   time: any;
+  originalTime: any;
   timepicker: any;
   timepicker: any;
   templating: any;
   templating: any;
+  originalTemplating: any;
   annotations: any;
   annotations: any;
   refresh: any;
   refresh: any;
   snapshot: any;
   snapshot: any;
@@ -68,8 +70,12 @@ export class DashboardModel {
     this.editable = data.editable !== false;
     this.editable = data.editable !== false;
     this.graphTooltip = data.graphTooltip || 0;
     this.graphTooltip = data.graphTooltip || 0;
     this.time = data.time || { from: 'now-6h', to: 'now' };
     this.time = data.time || { from: 'now-6h', to: 'now' };
+    this.originalTime = _.cloneDeep(this.time);
     this.timepicker = data.timepicker || {};
     this.timepicker = data.timepicker || {};
     this.templating = this.ensureListExist(data.templating);
     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.annotations = this.ensureListExist(data.annotations);
     this.refresh = data.refresh;
     this.refresh = data.refresh;
     this.snapshot = data.snapshot;
     this.snapshot = data.snapshot;
@@ -130,7 +136,12 @@ export class DashboardModel {
   }
   }
 
 
   // cleans meta data and other non persistent state
   // cleans meta data and other non persistent state
-  getSaveModelClone() {
+  getSaveModelClone(options?) {
+    let defaults = _.defaults(options || {}, {
+      saveVariables: false,
+      saveTimerange: false,
+    });
+
     // make clone
     // make clone
     var copy: any = {};
     var copy: any = {};
     for (var property in this) {
     for (var property in this) {
@@ -142,10 +153,23 @@ export class DashboardModel {
     }
     }
 
 
     // get variable save models
     // get variable save models
+    //console.log(this.templating.list);
     copy.templating = {
     copy.templating = {
       list: _.map(this.templating.list, variable => (variable.getSaveModel ? variable.getSaveModel() : variable)),
       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
     // get panel save models
     copy.panels = _.chain(this.panels)
     copy.panels = _.chain(this.panels)
       .filter(panel => panel.type !== 'add-panel')
       .filter(panel => panel.type !== 'add-panel')

+ 59 - 6
public/app/features/dashboard/save_modal.ts

@@ -1,4 +1,5 @@
 import coreModule from 'app/core/core_module';
 import coreModule from 'app/core/core_module';
+import _ from 'lodash';
 
 
 const template = `
 const template = `
 <div class="modal-body">
 <div class="modal-body">
@@ -14,19 +15,29 @@ const template = `
   </div>
   </div>
 
 
   <form name="ctrl.saveForm" ng-submit="ctrl.save()" class="modal-content" novalidate>
   <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">
       <div class="gf-form">
         <label class="gf-form-hint">
         <label class="gf-form-hint">
           <input
           <input
             type="text"
             type="text"
             name="message"
             name="message"
             class="gf-form-input"
             class="gf-form-input"
-            placeholder="Updates to &hellip;"
+            placeholder="Add a note to describe your changes &hellip;"
             give-focus="true"
             give-focus="true"
             ng-model="ctrl.message"
             ng-model="ctrl.message"
             ng-model-options="{allowInvalid: true}"
             ng-model-options="{allowInvalid: true}"
             ng-maxlength="this.max"
             ng-maxlength="this.max"
+            maxlength="64"
             autocomplete="off" />
             autocomplete="off" />
           <small class="gf-form-hint-text muted" ng-cloak>
           <small class="gf-form-hint-text muted" ng-cloak>
             <span ng-class="{'text-error': ctrl.saveForm.message.$invalid && ctrl.saveForm.message.$dirty }">
             <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">
     <div class="gf-form-button-row text-center">
       <button type="submit" class="btn btn-success" ng-disabled="ctrl.saveForm.$invalid">Save</button>
       <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>
     </div>
   </form>
   </form>
 </div>
 </div>
@@ -48,14 +59,51 @@ const template = `
 
 
 export class SaveDashboardModalCtrl {
 export class SaveDashboardModalCtrl {
   message: string;
   message: string;
+  saveVariables = false;
+  saveTimerange = false;
+  templating: any;
+  time: any;
+  originalTime: any;
+  current = [];
+  originalCurrent = [];
   max: number;
   max: number;
   saveForm: any;
   saveForm: any;
   dismiss: () => void;
   dismiss: () => void;
+  timeChange = false;
+  variableChange = false;
 
 
   /** @ngInject */
   /** @ngInject */
   constructor(private dashboardSrv) {
   constructor(private dashboardSrv) {
     this.message = '';
     this.message = '';
     this.max = 64;
     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() {
   save() {
@@ -63,9 +111,14 @@ export class SaveDashboardModalCtrl {
       return;
       return;
     }
     }
 
 
+    var options = {
+      saveVariables: this.saveVariables,
+      saveTimerange: this.saveTimerange,
+      message: this.message,
+    };
+
     var dashboard = this.dashboardSrv.getCurrent();
     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);
     return this.dashboardSrv.save(saveModel, options).then(this.dismiss);
   }
   }

+ 59 - 0
public/app/features/dashboard/specs/dashboard_model.jest.ts

@@ -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');
+    });
+  });
 });
 });

+ 90 - 0
public/app/features/dashboard/specs/save_modal.jest.ts

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