diff --git a/public/app/core/components/search/search.ts b/public/app/core/components/search/search.ts
index 728b6a3ee18..f8dc871e192 100644
--- a/public/app/core/components/search/search.ts
+++ b/public/app/core/components/search/search.ts
@@ -94,13 +94,11 @@ export class SearchCtrl {
return query.query === '' && query.starred === false && query.tag.length === 0;
}
- filterByTag(tag, evt) {
- this.query.tag.push(tag);
- this.search();
- this.giveSearchFocus = this.giveSearchFocus + 1;
- if (evt) {
- evt.stopPropagation();
- evt.preventDefault();
+ filterByTag(tag) {
+ if (_.indexOf(this.query.tag, tag) === -1) {
+ this.query.tag.push(tag);
+ this.search();
+ this.giveSearchFocus = this.giveSearchFocus + 1;
}
}
diff --git a/public/app/core/components/search/search_results.html b/public/app/core/components/search/search_results.html
new file mode 100644
index 00000000000..582fbe2b059
--- /dev/null
+++ b/public/app/core/components/search/search_results.html
@@ -0,0 +1,43 @@
+
\ No newline at end of file
diff --git a/public/app/core/components/search/search_results.jest.ts b/public/app/core/components/search/search_results.jest.ts
new file mode 100644
index 00000000000..ab24242860e
--- /dev/null
+++ b/public/app/core/components/search/search_results.jest.ts
@@ -0,0 +1,75 @@
+import { SearchResultsCtrl } from './search_results';
+
+describe('SearchResultsCtrl', () => {
+ let ctrl;
+
+ describe('when checking an item that is not checked', () => {
+ let item = {checked: false};
+ let selectionChanged = false;
+
+ beforeEach(() => {
+ ctrl = new SearchResultsCtrl();
+ ctrl.onSelectionChanged = () => selectionChanged = true;
+ ctrl.toggleSelection(item);
+ });
+
+ it('should set checked to true', () => {
+ expect(item.checked).toBeTruthy();
+ });
+
+ it('should trigger selection changed callback', () => {
+ expect(selectionChanged).toBeTruthy();
+ });
+ });
+
+ describe('when checking an item that is checked', () => {
+ let item = {checked: true};
+ let selectionChanged = false;
+
+ beforeEach(() => {
+ ctrl = new SearchResultsCtrl();
+ ctrl.onSelectionChanged = () => selectionChanged = true;
+ ctrl.toggleSelection(item);
+ });
+
+ it('should set checked to false', () => {
+ expect(item.checked).toBeFalsy();
+ });
+
+ it('should trigger selection changed callback', () => {
+ expect(selectionChanged).toBeTruthy();
+ });
+ });
+
+ describe('when selecting a tag', () => {
+ let selectedTag = null;
+
+ beforeEach(() => {
+ ctrl = new SearchResultsCtrl();
+ ctrl.onTagSelected = (tag) => selectedTag = tag;
+ ctrl.selectTag('tag-test');
+ });
+
+ it('should trigger tag selected callback', () => {
+ expect(selectedTag["$tag"]).toBe('tag-test');
+ });
+ });
+
+ describe('when toggle a folder', () => {
+ let folderToggled = false;
+ let folder = {
+ toggle: () => {
+ folderToggled = true;
+ }
+ };
+
+ beforeEach(() => {
+ ctrl = new SearchResultsCtrl();
+ ctrl.toggleFolderExpand(folder);
+ });
+
+ it('should trigger folder toggle callback', () => {
+ expect(folderToggled).toBeTruthy();
+ });
+ });
+});
diff --git a/public/app/core/components/search/search_results.ts b/public/app/core/components/search/search_results.ts
new file mode 100644
index 00000000000..de34d5bd5d0
--- /dev/null
+++ b/public/app/core/components/search/search_results.ts
@@ -0,0 +1,56 @@
+// import _ from 'lodash';
+import coreModule from '../../core_module';
+
+export class SearchResultsCtrl {
+ results: any;
+ onSelectionChanged: any;
+ onTagSelected: any;
+
+ toggleFolderExpand(section) {
+ if (section.toggle) {
+ section.toggle(section);
+ }
+ }
+
+ toggleSelection(item, evt) {
+ item.checked = !item.checked;
+
+ if (this.onSelectionChanged) {
+ this.onSelectionChanged();
+ }
+
+ if (evt) {
+ evt.stopPropagation();
+ evt.preventDefault();
+ }
+ }
+
+ selectTag(tag, evt) {
+ if (this.onTagSelected) {
+ this.onTagSelected({$tag: tag});
+ }
+
+ if (evt) {
+ evt.stopPropagation();
+ evt.preventDefault();
+ }
+ }
+}
+
+export function searchResultsDirective() {
+ return {
+ restrict: 'E',
+ templateUrl: 'public/app/core/components/search/search_results.html',
+ controller: SearchResultsCtrl,
+ bindToController: true,
+ controllerAs: 'ctrl',
+ scope: {
+ editable: '@',
+ results: '=',
+ onSelectionChanged: '&',
+ onTagSelected: '&'
+ },
+ };
+}
+
+coreModule.directive('dashboardSearchResults', searchResultsDirective);
diff --git a/public/app/core/core.ts b/public/app/core/core.ts
index 602cb49cb68..a1a93c3cdcc 100644
--- a/public/app/core/core.ts
+++ b/public/app/core/core.ts
@@ -52,6 +52,7 @@ import {gfPageDirective} from './components/gf_page';
import {orgSwitcher} from './components/org_switcher';
import {profiler} from './profiler';
import {registerAngularDirectives} from './angular_wrappers';
+import {searchResultsDirective} from './components/search/search_results';
export {
profiler,
@@ -83,5 +84,6 @@ export {
userGroupPicker,
geminiScrollbar,
gfPageDirective,
- orgSwitcher
+ orgSwitcher,
+ searchResultsDirective
};
diff --git a/public/app/core/services/search_srv.ts b/public/app/core/services/search_srv.ts
index d7c0e75de5b..fdd9b8ca77f 100644
--- a/public/app/core/services/search_srv.ts
+++ b/public/app/core/services/search_srv.ts
@@ -128,14 +128,20 @@ export class SearchSrv {
});
}
- private browse() {
+ private browse(options) {
let sections: any = {};
- let promises = [
- this.getRecentDashboards(sections),
- this.getStarred(sections),
- this.getDashboardsAndFolders(sections),
- ];
+ let promises = [];
+
+ if (!options.skipRecent) {
+ promises.push(this.getRecentDashboards(sections));
+ }
+
+ if (!options.skipStarred) {
+ promises.push(this.getStarred(sections));
+ }
+
+ promises.push(this.getDashboardsAndFolders(sections));
return this.$q.all(promises).then(() => {
return _.sortBy(_.values(sections), 'score');
@@ -149,7 +155,7 @@ export class SearchSrv {
search(options) {
if (!options.query && (!options.tag || options.tag.length === 0) && !options.starred) {
- return this.browse();
+ return this.browse(options);
}
let query = _.clone(options);
@@ -157,6 +163,10 @@ export class SearchSrv {
query.type = 'dash-db';
return this.backendSrv.search(query).then(results => {
+ if (results.length === 0) {
+ return results;
+ }
+
let section = {
hideHeader: true,
items: [],
diff --git a/public/app/core/specs/search_srv.jest.ts b/public/app/core/specs/search_srv.jest.ts
index 0624618a224..649cfbf00dd 100644
--- a/public/app/core/specs/search_srv.jest.ts
+++ b/public/app/core/specs/search_srv.jest.ts
@@ -2,6 +2,7 @@ import { SearchSrv } from 'app/core/services/search_srv';
import { BackendSrvMock } from 'test/mocks/backend_srv';
import impressionSrv from 'app/core/services/impression_srv';
import { contextSrv } from 'app/core/services/context_srv';
+import { beforeEach } from 'test/lib/common';
jest.mock('app/core/store', () => {
return {
@@ -244,4 +245,43 @@ describe('SearchSrv', () => {
expect(backendSrvMock.search.mock.calls[0][0].starred).toEqual(true);
});
});
+
+ describe('when skipping recent dashboards', () => {
+ let getRecentDashboardsCalled = false;
+
+ beforeEach(() => {
+ backendSrvMock.search = jest.fn();
+ backendSrvMock.search.mockReturnValue(Promise.resolve([]));
+
+ searchSrv.getRecentDashboards = () => {
+ getRecentDashboardsCalled = true;
+ };
+
+ return searchSrv.search({ skipRecent: true }).then(() => {});
+ });
+
+ it('should not fetch recent dashboards', () => {
+ expect(getRecentDashboardsCalled).toBeFalsy();
+ });
+ });
+
+ describe('when skipping starred dashboards', () => {
+ let getStarredCalled = false;
+
+ beforeEach(() => {
+ backendSrvMock.search = jest.fn();
+ backendSrvMock.search.mockReturnValue(Promise.resolve([]));
+ impressionSrv.getDashboardOpened = jest.fn().mockReturnValue([]);
+
+ searchSrv.getStarred = () => {
+ getStarredCalled = true;
+ };
+
+ return searchSrv.search({ skipStarred: true }).then(() => {});
+ });
+
+ it('should not fetch starred dashboards', () => {
+ expect(getStarredCalled).toBeFalsy();
+ });
+ });
});
diff --git a/public/app/features/dashboard/dashboard_list_ctrl.ts b/public/app/features/dashboard/dashboard_list_ctrl.ts
index 93e9df84c15..59a90426be3 100644
--- a/public/app/features/dashboard/dashboard_list_ctrl.ts
+++ b/public/app/features/dashboard/dashboard_list_ctrl.ts
@@ -18,7 +18,7 @@ export class DashboardListCtrl {
/** @ngInject */
constructor(private backendSrv, navModelSrv, private $q, private searchSrv: SearchSrv) {
this.navModel = navModelSrv.getNav('dashboards', 'dashboards', 0);
- this.query = {query: '', mode: 'tree', tag: [], starred: false};
+ this.query = {query: '', mode: 'tree', tag: [], starred: false, skipRecent: true, skipStarred: true};
this.selectedStarredFilter = this.starredFilterOptions[0];
this.getDashboards().then(() => {
@@ -148,11 +148,9 @@ export class DashboardListCtrl {
});
}
- filterByTag(tag, evt) {
- this.query.tag.push(tag);
- if (evt) {
- evt.stopPropagation();
- evt.preventDefault();
+ filterByTag(tag) {
+ if (_.indexOf(this.query.tag, tag) === -1) {
+ this.query.tag.push(tag);
}
return this.getDashboards();
@@ -163,9 +161,9 @@ export class DashboardListCtrl {
}
onTagFilterChange() {
- this.query.tag.push(this.selectedTagFilter.term);
+ var res = this.filterByTag(this.selectedTagFilter.term);
this.selectedTagFilter = this.tagFilterOptions[0];
- return this.getDashboards();
+ return res;
}
removeTag(tag, evt) {
diff --git a/public/app/features/dashboard/partials/dashboardList.html b/public/app/features/dashboard/partials/dashboardList.html
index 8bb46ebd127..8a994f12c8c 100644
--- a/public/app/features/dashboard/partials/dashboardList.html
+++ b/public/app/features/dashboard/partials/dashboardList.html
@@ -78,54 +78,13 @@
/>