Explorar o código

dashlist: started fixing js/css after design changes

Daniel Lee %!s(int64=8) %!d(string=hai) anos
pai
achega
8daff73ba2

+ 2 - 1
public/app/core/services/search_srv.ts

@@ -24,6 +24,7 @@ export class SearchSrv {
             items: [],
             icon: 'fa fa-folder',
             score: _.keys(sections).length,
+            uri: hit.uri
           };
         }
       }
@@ -34,7 +35,7 @@ export class SearchSrv {
         items: [],
         icon: 'fa fa-folder-open',
         score: _.keys(sections).length,
-        expanded: true,
+        expanded: true
       };
 
       for (let hit of results) {

+ 57 - 67
public/app/features/dashboard/dashboard_list_ctrl.ts

@@ -1,101 +1,74 @@
 import _ from 'lodash';
 import appEvents from 'app/core/app_events';
+import { SearchSrv } from 'app/core/services/search_srv';
 
 export class DashboardListCtrl {
-  public dashboards: any [];
+  public sections: any [];
+  tags: any [];
   query: any;
   navModel: any;
   canDelete = false;
   canMove = false;
 
   /** @ngInject */
-  constructor(private backendSrv, navModelSrv, private $q) {
+  constructor(private backendSrv, navModelSrv, private $q, private searchSrv: SearchSrv) {
     this.navModel = navModelSrv.getNav('dashboards', 'dashboards');
     this.query = {query: '', mode: 'tree', tag: []};
+
     this.getDashboards();
+    // this.getDashboards().then(() => {
+    //   this.getTags();
+    // });
   }
 
   getDashboards() {
-    return this.backendSrv.search(this.query).then((result) => {
-
-      this.dashboards = this.groupDashboardsInFolders(result);
+    return this.searchSrv.browse().then((result) => {
 
-      for (let dash of this.dashboards) {
-        dash.checked = false;
-      }
-    });
-  }
+      this.sections = result;
 
-  groupDashboardsInFolders(results) {
-    let byId = _.groupBy(results, 'id');
-    let byFolderId = _.groupBy(results, 'folderId');
-    let finalList = [];
-
-    // add missing parent folders
-    _.each(results, (hit, index) => {
-      if (hit.folderId && !byId[hit.folderId]) {
-        const folder = {
-          id: hit.folderId,
-          uri: `db/${hit.folderSlug}`,
-          title: hit.folderTitle,
-          type: 'dash-folder'
-        };
-        byId[hit.folderId] = folder;
-        results.splice(index, 0, folder);
-      }
-    });
+      for (let section of this.sections) {
+        section.checked = false;
 
-    // group by folder
-    for (let hit of results) {
-      if (hit.folderId) {
-        hit.type = "dash-child";
-      } else {
-        finalList.push(hit);
-      }
-
-      hit.url = 'dashboard/' + hit.uri;
-
-      if (hit.type === 'dash-folder') {
-        if (!byFolderId[hit.id]) {
-          continue;
-        }
-
-        for (let child of byFolderId[hit.id]) {
-          finalList.push(child);
+        for (let dashboard of section.items) {
+          dashboard.checked = false;
         }
       }
-    }
-
-    return finalList;
+    });
   }
 
   selectionChanged() {
-    const selected = _.filter(this.dashboards, {checked: true}).length;
-    this.canDelete = selected > 0;
 
-    const selectedDashboards = _.filter(this.dashboards, (o) => {
-      return o.checked && (o.type === 'dash-db' || o.type === 'dash-child');
-    }).length;
+    let selectedDashboards = 0;
+
+    for (let section of this.sections) {
+      selectedDashboards += _.filter(section.items, {checked: true}).length;
+    }
 
-    const selectedFolders = _.filter(this.dashboards, {checked: true, type: 'dash-folder'}).length;
+    const selectedFolders = _.filter(this.sections, {checked: true}).length;
     this.canMove = selectedDashboards > 0 && selectedFolders === 0;
+    this.canDelete = selectedDashboards > 0 || selectedFolders > 0;
   }
 
   getDashboardsToDelete() {
-    const selectedFolderIds = this.getFolderIds(this.dashboards);
-    return _.filter(this.dashboards, o => {
-      return o.checked && (
-        o.type !== 'dash-child' ||
-        (o.type === 'dash-child' && !_.includes(selectedFolderIds, o.folderId))
-      );
-    });
+    let selectedDashboards = [];
+
+    for (const section of this.sections) {
+      if (section.checked) {
+        selectedDashboards.push(section.uri);
+      } else {
+        const selected = _.filter(section.items, {checked: true});
+        selectedDashboards.push(... _.map(selected, 'uri'));
+      }
+    }
+
+    return selectedDashboards;
   }
 
-  getFolderIds(dashboards) {
+  getFolderIds(sections) {
     const ids = [];
-    for (let dash of dashboards) {
-      if (dash.type === 'dash-folder') {
-        ids.push(dash.id);
+    for (let s of sections) {
+      if (s.checked) {
+        ids.push(s.id);
       }
     }
     return ids;
@@ -112,7 +85,7 @@ export class DashboardListCtrl {
       onConfirm: () => {
         const promises = [];
         for (let dash of selectedDashboards) {
-          promises.push(this.backendSrv.delete(`/api/dashboards/${dash.uri}`));
+          promises.push(this.backendSrv.delete(`/api/dashboards/${dash}`));
         }
 
         this.$q.all(promises).then(() => {
@@ -122,8 +95,19 @@ export class DashboardListCtrl {
     });
   }
 
+  getDashboardsToMove() {
+    let selectedDashboards = [];
+
+    for (const section of this.sections) {
+      const selected = _.filter(section.items, {checked: true});
+      selectedDashboards.push(... _.map(selected, 'uri'));
+    }
+
+    return selectedDashboards;
+  }
+
   moveTo() {
-    const selectedDashboards =  _.filter(this.dashboards, {checked: true});
+    const selectedDashboards = this.getDashboardsToMove();
 
     const template = '<move-to-folder-modal dismiss="dismiss()" ' +
       'dashboards="model.dashboards" after-save="model.afterSave()">' +
@@ -135,6 +119,12 @@ export class DashboardListCtrl {
     });
   }
 
+  // getTags() {
+  //   return this.backendSrv.get('/api/dashboards/tags').then((results) => {
+  //     this.tags = results;
+  //   });
+  // }
+
   filterByTag(tag, evt) {
     this.query.tag.push(tag);
     this.getDashboards();

+ 1 - 1
public/app/features/dashboard/move_to_folder_modal/move_to_folder.ts

@@ -18,7 +18,7 @@ export class MoveToFolderCtrl {
   save() {
     const promises = [];
     for (let dash of this.dashboards) {
-      const promise = this.backendSrv.get('/api/dashboards/' + dash.uri).then(fullDash => {
+      const promise = this.backendSrv.get('/api/dashboards/' + dash).then(fullDash => {
         const model = new DashboardModel(fullDash.dashboard, fullDash.meta);
         model.folderId = this.folder.id;
         model.meta.folderId = this.folder.id;

+ 78 - 45
public/app/features/dashboard/partials/dashboardList.html

@@ -15,26 +15,25 @@
         </a>
       </div>
       <div class="gf-form-group">
-        <div class="gf-form-inline">
-          <div class="gf-form width-15">
-            <span style="position: relative;">
-              <input type="text" class="gf-form-input" placeholder="Find Dashboard by name" tabindex="1" give-focus="true"
-                ng-model="ctrl.query.query" ng-model-options="{ debounce: 500 }" spellcheck='false' ng-change="ctrl.getDashboards()" />
-            </span>
-          </div>
-          <div class="gf-form" ng-if="ctrl.query.tag.length">
-            Filtered by Tags:
-            <span ng-repeat="tagName in ctrl.query.tag">
-              <a ng-click="ctrl.removeTag(tagName, $event)" tag-color-from-name="tagName" class="label label-tag">
-                <i class="fa fa-remove"></i>
-                {{tagName}}
-              </a>
-            </span>
-          </div>
+        <div class="gf-form width-15">
+          <span style="position: relative;">
+            <input type="text" class="gf-form-input" placeholder="Find Dashboard by name" tabindex="1" give-focus="true"
+              ng-model="ctrl.query.query" ng-model-options="{ debounce: 500 }" spellcheck='false' ng-change="ctrl.getDashboards()" />
+          </span>
         </div>
       </div>
 
-      <div class="gf-form-group" ng-if="ctrl.dashboards.length > 1">
+      <div class="gf-form" ng-if="ctrl.query.tag.length">
+        Filters:
+        <span ng-repeat="tagName in ctrl.query.tag">
+          <a ng-click="ctrl.removeTag(tagName, $event)" tag-color-from-name="tagName" class="label label-tag">
+            <i class="fa fa-remove"></i>
+            {{tagName}}
+          </a>
+        </span>
+      </div>
+
+      <div class="gf-form-group">
         <div class="gf-form-button-row">
           <button	type="button"
               class="btn gf-form-button btn-secondary"
@@ -54,40 +53,74 @@
 
         <div class="admin-list-table" style="height: 80%">
           <div gemini-scrollbar>
-            <table class="filter-table form-inline" ng-show="ctrl.dashboards.length > 0">
-              <thead>
-                <tr>
-                  <th class="width-4"></th>
-                  <th></th>
-                </tr>
-              </thead>
-              <tbody>
-                <tr bindonce ng-repeat="dashboard in ctrl.dashboards">
-                  <td class="filter-table__switch-cell" bs-tooltip="" data-placement="right">
+            <div ng-show="ctrl.sections.length > 0">
+              <!-- <div>
+                <select class="gf-form-input" ng-model="ctrl.query.tags" ng-options="t.term for t in ctrl.tags" />
+              </div> -->
+              <div ng-repeat="section in ctrl.sections" class="search-section">
+                <gf-form-switch
+                  switch-class="gf-form-switch--table-cell"
+                  on-change="ctrl.selectionChanged()"
+                  checked="section.checked">
+                </gf-form-switch>
+                <a class="search-section__header pointer" ng-show="::section.title" ng-click="section.collapsed = !section.collapsed">
+                  <i class="search-section__header__icon" ng-class="section.icon"></i>
+                  <span class="search-section__header__text">{{::section.title}}</span>
+                  <i class="fa fa-minus search-section__header__toggle" ng-hide="section.collapsed"></i>
+                  <i class="fa fa-plus search-section__header__toggle" ng-show="section.collapsed"></i>
+                </a>
+
+                <div ng-if="!section.collapsed">
+                  <div ng-repeat="item in section.items" class="search-item" ng-class="{'selected': item.selected}">
                     <gf-form-switch
-                        switch-class="gf-form-switch--table-cell"
-                        on-change="ctrl.selectionChanged()"
-                        checked="dashboard.checked">
-                    </gf-form-switch>
-                  </td>
-                  <td>
-                    <a class="search-item pointer search-item--{{dashboard.type}}"
-                      bo-href-i="{{dashboard.url}}">
-                      <span class="search-result-tags">
-                        <span ng-click="ctrl.filterByTag(tag, $event)" bindonce ng-repeat="tag in dashboard.tags" tag-color-from-name="tag"  class="label label-tag">
+                      switch-class="gf-form-switch--table-cell"
+                      on-change="ctrl.selectionChanged()"
+                      checked="item.checked" />
+                    <a ng-href="{{::item.url}}">
+                      <span class="search-item__icon">
+                        <i class="fa fa-th-large"></i>
+                      </span>
+                      <span class="search-item__title">
+                        {{::item.title}}
+                      </span>
+                      <span class="search-item__tags">
+                        <span ng-click="ctrl.filterByTag(tag, $event)" ng-repeat="tag in item.tags" tag-color-from-name="tag"  class="label label-tag">
                           {{tag}}
                         </span>
-                        <i class="fa" bo-class="{'fa-star': dashboard.isStarred, 'fa-star-o': !dashboard.isStarred}"></i>
                       </span>
-                      <span class="search-result-link">
-                        <i class="fa search-result-icon"></i>
-                        <span bo-text="dashboard.title" />
+                      <span class="search-item__actions">
+                        <i class="fa" ng-class="{'fa-star': item.isStarred, 'fa-star-o': !item.isStarred}"></i>
                       </span>
                     </a>
-                  </td>
-                </tr>
-              </tbody>
-            </table>
+                  </div>
+                </div>
+              </div>
+            </div>
+
+              <!-- <div bindonce class="search-section" ng-repeat="dashboard in ctrl.dashboards">
+                  <gf-form-switch
+                      switch-class="gf-form-switch--table-cell"
+                      on-change="ctrl.selectionChanged()"
+                      checked="dashboard.checked">
+                  </gf-form-switch>
+                  <a class="search-item pointer "
+                    bo-href-i="{{dashboard.url}}">
+                    <span class="search-item__icon">
+                      <i class="fa fa-th-large"></i>
+                    </span>
+                    <span class="search-result-tags">
+                      <span ng-click="ctrl.filterByTag(tag, $event)" bindonce ng-repeat="tag in dashboard.tags" tag-color-from-name="tag"  class="label label-tag">
+                        {{tag}}
+                      </span>
+                      <i class="fa" bo-class="{'fa-star': dashboard.isStarred, 'fa-star-o': !dashboard.isStarred}"></i>
+                    </span>
+                    <span class="search-result-link">
+                      <i class="fa search-result-icon"></i>
+                      <span bo-text="dashboard.title" />
+                    </span>
+                  </a>
+              </div> -->
+            </div>
           </div>
         </div>
 

+ 204 - 71
public/app/features/dashboard/specs/dashboard_list_ctrl.jest.ts

@@ -1,76 +1,63 @@
 import {DashboardListCtrl} from '../dashboard_list_ctrl';
+import { SearchSrv } from 'app/core/services/search_srv';
 import q from 'q';
 
 describe('DashboardListCtrl', () => {
   let ctrl;
 
-  describe('when fetching dashboards', () => {
-    describe('and dashboard has parent that is not in search result', () => {
-      beforeEach(() => {
-        const response = [
-          {
-            id: 399,
-            title: "Dashboard Test",
-            uri: "db/dashboard-test",
-            type: "dash-db",
-            tags: [],
-            isStarred: false,
-            folderId: 410,
-            folderTitle: "afolder",
-            folderSlug: "afolder"
-          }
-        ];
-
-        ctrl = new DashboardListCtrl({search: () => q.resolve(response)}, {getNav: () => {}}, q);
-        return ctrl.getDashboards();
-      });
-
-      it('should add the missing parent folder to the result', () => {
-        expect(ctrl.dashboards.length).toEqual(2);
-        expect(ctrl.dashboards[0].id).toEqual(410);
-        expect(ctrl.dashboards[1].id).toEqual(399);
-      });
-    });
-
+  describe('when browsing dashboards', () => {
     beforeEach(() => {
       const response = [
         {
           id: 410,
           title: "afolder",
-          uri: "db/afolder",
           type: "dash-folder",
+          items: [
+            {
+              id: 399,
+              title: "Dashboard Test",
+              url: "dashboard/db/dashboard-test",
+              icon: 'fa fa-folder',
+              tags: [],
+              isStarred: false,
+              folderId: 410,
+              folderTitle: "afolder",
+              folderSlug: "afolder"
+            }
+          ],
           tags: [],
           isStarred: false
         },
         {
-          id: 3,
-          title: "something else",
+          id: 0,
+          title: "Root",
+          icon: 'fa fa-folder-open',
           uri: "db/something-else",
           type: "dash-db",
+          items: [
+            {
+              id: 500,
+              title: "Dashboard Test",
+              url: "dashboard/db/dashboard-test",
+              icon: 'fa fa-folder',
+              tags: [],
+              isStarred: false
+            }
+          ],
           tags: [],
           isStarred: false,
-        },
-        {
-          id: 399,
-          title: "Dashboard Test",
-          uri: "db/dashboard-test",
-          type: "dash-db",
-          tags: [],
-          isStarred: false,
-          folderId: 410,
-          folderTitle: "afolder",
-          folderSlug: "afolder"
         }
       ];
-      ctrl = new DashboardListCtrl({search: () => q.resolve(response)}, {getNav: () => {}}, null);
+      ctrl = createCtrlWithStubs(response);
       return ctrl.getDashboards();
     });
 
-    it('should group them in folders', () => {
-      expect(ctrl.dashboards.length).toEqual(3);
-      expect(ctrl.dashboards[0].id).toEqual(410);
-      expect(ctrl.dashboards[1].id).toEqual(399);
-      expect(ctrl.dashboards[2].id).toEqual(3);
+    it('should set checked to false on all sections and children', () => {
+      expect(ctrl.sections.length).toEqual(2);
+      expect(ctrl.sections[0].checked).toEqual(false);
+      expect(ctrl.sections[0].items[0].checked).toEqual(false);
+      expect(ctrl.sections[1].checked).toEqual(false);
+      expect(ctrl.sections[1].items[0].checked).toEqual(false);
     });
   });
 
@@ -78,14 +65,26 @@ describe('DashboardListCtrl', () => {
     let ctrl;
 
     beforeEach(() => {
-      ctrl = new DashboardListCtrl({search: () => q.resolve([])}, {getNav: () => {}}, null);
+      ctrl = createCtrlWithStubs([]);
     });
 
     describe('and no dashboards are selected', () => {
       beforeEach(() => {
-        ctrl.dashboards = [
-          {id: 1, type: 'dash-folder'},
-          {id: 2, type: 'dash-db'}
+        ctrl.sections = [
+          {
+            id: 1,
+            items: [
+              { id: 2, checked: false }
+            ],
+            checked: false
+          },
+          {
+            id: 0,
+            items: [
+              { id: 3, checked: false }
+            ],
+            checked: false
+          }
         ];
         ctrl.selectionChanged();
       });
@@ -101,9 +100,23 @@ describe('DashboardListCtrl', () => {
 
     describe('and one dashboard in root is selected', () => {
       beforeEach(() => {
-        ctrl.dashboards = [
-          {id: 1, type: 'dash-folder'},
-          {id: 2, type: 'dash-db', checked: true}
+        ctrl.sections = [
+          {
+            id: 1,
+            title: 'folder',
+            items: [
+              { id: 2, checked: false }
+            ],
+            checked: false
+          },
+          {
+            id: 0,
+            title: 'Root',
+            items: [
+              { id: 3, checked: true }
+            ],
+            checked: false
+          }
         ];
         ctrl.selectionChanged();
       });
@@ -119,10 +132,25 @@ describe('DashboardListCtrl', () => {
 
     describe('and one child dashboard is selected', () => {
       beforeEach(() => {
-        ctrl.dashboards = [
-          {id: 1, type: 'dash-folder'},
-          {id: 2, type: 'dash-child', checked: true}
+        ctrl.sections = [
+          {
+            id: 1,
+            title: 'folder',
+            items: [
+              { id: 2, checked: true }
+            ],
+            checked: false
+          },
+          {
+            id: 0,
+            title: 'Root',
+            items: [
+              { id: 3, checked: false }
+            ],
+            checked: false
+          }
         ];
+
         ctrl.selectionChanged();
       });
 
@@ -137,10 +165,25 @@ describe('DashboardListCtrl', () => {
 
     describe('and one child dashboard and one dashboard is selected', () => {
       beforeEach(() => {
-        ctrl.dashboards = [
-          {id: 1, type: 'dash-folder'},
-          {id: 2, type: 'dash-child', checked: true}
+        ctrl.sections = [
+          {
+            id: 1,
+            title: 'folder',
+            items: [
+              { id: 2, checked: true }
+            ],
+            checked: false
+          },
+          {
+            id: 0,
+            title: 'Root',
+            items: [
+              { id: 3, checked: true }
+            ],
+            checked: false
+          }
         ];
+
         ctrl.selectionChanged();
       });
 
@@ -155,10 +198,33 @@ describe('DashboardListCtrl', () => {
 
     describe('and one child dashboard and one folder is selected', () => {
       beforeEach(() => {
-        ctrl.dashboards = [
-          {id: 1, type: 'dash-folder', checked: true},
-          {id: 2, type: 'dash-child', checked: true}
+        ctrl.sections = [
+          {
+            id: 1,
+            title: 'folder',
+            items: [
+              { id: 2, checked: false }
+            ],
+            checked: true
+          },
+          {
+            id: 3,
+            title: 'folder',
+            items: [
+              { id: 4, checked: true }
+            ],
+            checked: false
+          },
+          {
+            id: 0,
+            title: 'Root',
+            items: [
+              { id: 3, checked: false }
+            ],
+            checked: false
+          }
         ];
+
         ctrl.selectionChanged();
       });
 
@@ -174,19 +240,86 @@ describe('DashboardListCtrl', () => {
 
   describe('when deleting dashboards', () => {
     beforeEach(() => {
-      ctrl = new DashboardListCtrl({search: () => q.resolve([])}, {getNav: () => {}}, q);
-      ctrl.dashboards = [
-        {id: 1, type: 'dash-folder', checked: true},
-        {id: 2, type: 'dash-child', checked: true, folderId: 1},
-        {id: 3, type: 'dash-db', checked: true}
+      ctrl = createCtrlWithStubs([]);
+
+      ctrl.sections = [
+        {
+          id: 1,
+          title: 'folder',
+          items: [
+            { id: 2, checked: true, uri: 'dash' }
+          ],
+          checked: true,
+          uri: 'folder'
+        },
+        {
+          id: 0,
+          title: 'Root',
+          items: [
+            { id: 3, checked: true, uri: 'dash-2' }
+          ],
+          checked: false
+        }
       ];
     });
 
     it('should filter out children if parent is selected', () => {
       const toBeDeleted = ctrl.getDashboardsToDelete();
       expect(toBeDeleted.length).toEqual(2);
-      expect(toBeDeleted[0].id).toEqual(1);
-      expect(toBeDeleted[1].id).toEqual(3);
+      expect(toBeDeleted[0]).toEqual('folder');
+      expect(toBeDeleted[1]).toEqual('dash-2');
+    });
+  });
+
+  describe('when moving dashboards', () => {
+    beforeEach(() => {
+      ctrl = createCtrlWithStubs([]);
+
+      ctrl.sections = [
+        {
+          id: 1,
+          title: 'folder',
+          items: [
+            { id: 2, checked: true, uri: 'dash' }
+          ],
+          checked: false,
+          uri: 'folder'
+        },
+        {
+          id: 0,
+          title: 'Root',
+          items: [
+            { id: 3, checked: true, uri: 'dash-2' }
+          ],
+          checked: false
+        }
+      ];
+    });
+
+    it('should get selected dashboards', () => {
+      const toBeMove = ctrl.getDashboardsToMove();
+      expect(toBeMove.length).toEqual(2);
+      expect(toBeMove[0]).toEqual('dash');
+      expect(toBeMove[1]).toEqual('dash-2');
     });
   });
 });
+
+function createCtrlWithStubs(response: any) {
+  const searchSrvStub = {
+    browse: () => {
+      return  q.resolve(response);
+    },
+    search: (options: any) => {
+      return  q.resolve(response);
+    },
+    toggleFolder: (section) => {
+      return  q.resolve(response);
+    },
+    getDashboardTags: () => {
+      return  q.resolve([]);
+    }
+  };
+
+  return new DashboardListCtrl({}, {getNav: () => {}}, q, <SearchSrv>searchSrvStub);
+}