Commit 8daff73b by Daniel Lee

dashlist: started fixing js/css after design changes

parent afa226be
......@@ -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) {
......
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();
......
......@@ -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;
......
......@@ -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>
......
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);
}
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