mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Search: cleanup old Angular files (#23860)
* Search: Remove wrapperRef * Search: Remove angular search files * Search: Unify search types * Search: Remove redundant hideHeader prop * Search: Remove app/types/search.ts * Search: Update imports * Search: Fix type errors
This commit is contained in:
parent
58b566a252
commit
1f2a70117b
@ -4,7 +4,8 @@ import { useAsyncFn } from 'react-use';
|
|||||||
import { SelectableValue } from '@grafana/data';
|
import { SelectableValue } from '@grafana/data';
|
||||||
import { AsyncSelect } from '@grafana/ui';
|
import { AsyncSelect } from '@grafana/ui';
|
||||||
import { backendSrv } from 'app/core/services/backend_srv';
|
import { backendSrv } from 'app/core/services/backend_srv';
|
||||||
import { DashboardSearchHit, DashboardDTO } from 'app/types';
|
import { DashboardSearchHit } from 'app/features/search/types';
|
||||||
|
import { DashboardDTO } from 'app/types';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
onSelected: (dashboard: DashboardDTO) => void;
|
onSelected: (dashboard: DashboardDTO) => void;
|
||||||
|
@ -5,7 +5,7 @@ import { debounce } from 'lodash';
|
|||||||
import appEvents from '../../app_events';
|
import appEvents from '../../app_events';
|
||||||
import { getBackendSrv } from '@grafana/runtime';
|
import { getBackendSrv } from '@grafana/runtime';
|
||||||
import { contextSrv } from 'app/core/services/context_srv';
|
import { contextSrv } from 'app/core/services/context_srv';
|
||||||
import { DashboardSearchHit } from '../../../types';
|
import { DashboardSearchHit } from 'app/features/search/types';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
onChange: ($folder: { title: string; id: number }) => void;
|
onChange: ($folder: { title: string; id: number }) => void;
|
||||||
|
@ -3,7 +3,7 @@ import React, { PureComponent } from 'react';
|
|||||||
import { InlineFormLabel, LegacyForms } from '@grafana/ui';
|
import { InlineFormLabel, LegacyForms } from '@grafana/ui';
|
||||||
const { Select } = LegacyForms;
|
const { Select } = LegacyForms;
|
||||||
|
|
||||||
import { DashboardSearchHit, DashboardSearchHitType } from 'app/types';
|
import { DashboardSearchHit, DashboardSearchItemType } from 'app/features/search/types';
|
||||||
import { backendSrv } from 'app/core/services/backend_srv';
|
import { backendSrv } from 'app/core/services/backend_srv';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -50,7 +50,7 @@ export class SharedPreferences extends PureComponent<Props, State> {
|
|||||||
id: 0,
|
id: 0,
|
||||||
title: 'Default',
|
title: 'Default',
|
||||||
tags: [],
|
tags: [],
|
||||||
type: '' as DashboardSearchHitType,
|
type: '' as DashboardSearchItemType,
|
||||||
uid: '',
|
uid: '',
|
||||||
uri: '',
|
uri: '',
|
||||||
url: '',
|
url: '',
|
||||||
@ -60,6 +60,7 @@ export class SharedPreferences extends PureComponent<Props, State> {
|
|||||||
folderUrl: '',
|
folderUrl: '',
|
||||||
isStarred: false,
|
isStarred: false,
|
||||||
slug: '',
|
slug: '',
|
||||||
|
items: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (prefs.homeDashboardId > 0 && !dashboards.find(d => d.id === prefs.homeDashboardId)) {
|
if (prefs.homeDashboardId > 0 && !dashboards.find(d => d.id === prefs.homeDashboardId)) {
|
||||||
|
@ -1,118 +0,0 @@
|
|||||||
<div class="dashboard-list">
|
|
||||||
<div
|
|
||||||
class="page-action-bar page-action-bar--narrow"
|
|
||||||
ng-hide="ctrl.folderId && !ctrl.hasFilters && ctrl.sections.length === 0"
|
|
||||||
>
|
|
||||||
<label class="gf-form gf-form--grow gf-form--has-input-icon">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="gf-form-input max-width-30"
|
|
||||||
placeholder="Search dashboards by name"
|
|
||||||
tabindex="1"
|
|
||||||
give-focus="true"
|
|
||||||
ng-model="ctrl.query.query"
|
|
||||||
ng-model-options="{ debounce: 500 }"
|
|
||||||
spellcheck="false"
|
|
||||||
ng-change="ctrl.onQueryChange()"
|
|
||||||
/>
|
|
||||||
<icon class="gf-form-input-icon" name="'search'" style="margin-top: -5px"></icon>
|
|
||||||
</label>
|
|
||||||
<div class="page-action-bar__spacer"></div>
|
|
||||||
<a
|
|
||||||
class="btn btn-primary"
|
|
||||||
ng-href="{{ctrl.createDashboardUrl()}}"
|
|
||||||
ng-if="ctrl.hasEditPermissionInFolders || ctrl.canSave"
|
|
||||||
>
|
|
||||||
New Dashboard
|
|
||||||
</a>
|
|
||||||
<a class="btn btn-primary" href="dashboards/folder/new" ng-if="!ctrl.folderId && ctrl.isEditor">
|
|
||||||
New Folder
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
class="btn btn-primary"
|
|
||||||
href="{{ctrl.importDashboardUrl()}}"
|
|
||||||
ng-if="ctrl.hasEditPermissionInFolders || ctrl.canSave"
|
|
||||||
>
|
|
||||||
Import
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="page-action-bar page-action-bar--narrow" ng-show="ctrl.hasFilters">
|
|
||||||
<div class="gf-form-inline">
|
|
||||||
<div class="gf-form" ng-show="ctrl.query.tag.length > 0">
|
|
||||||
<label class="gf-form-label width-4">
|
|
||||||
Tags
|
|
||||||
</label>
|
|
||||||
<div class="gf-form-input gf-form-input--plaintext" ng-show="ctrl.query.tag.length > 0">
|
|
||||||
<span ng-repeat="tagName in ctrl.query.tag">
|
|
||||||
<a ng-click="ctrl.removeTag(tagName, $event)" tag-color-from-name="tagName" class="tag label label-tag">
|
|
||||||
<icon name="'times'"></icon> {{tagName}}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form" ng-show="ctrl.query.starred">
|
|
||||||
<label class="gf-form-label">
|
|
||||||
<a class="pointer" ng-click="ctrl.removeStarred()"> <icon name="'check'"></icon> Starred </a>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label">
|
|
||||||
<a class="pointer" ng-click="ctrl.clearFilters()" bs-tooltip="'Clear current search query and filters'">
|
|
||||||
<icon name="'times'"></icon> Clear
|
|
||||||
</a>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="search-results" ng-show="ctrl.hasFilters && ctrl.sections.length === 0">
|
|
||||||
<em class="muted">
|
|
||||||
No dashboards matching your search were found.
|
|
||||||
</em>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="search-results" ng-show="!ctrl.folderId && !ctrl.hasFilters && ctrl.sections.length === 0">
|
|
||||||
<em class="muted">
|
|
||||||
No dashboards found.
|
|
||||||
</em>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="search-results" ng-show="ctrl.sections.length > 0">
|
|
||||||
<search-filters
|
|
||||||
on-select-all-changed="ctrl.onSelectAllChanged"
|
|
||||||
all-checked="ctrl.selectAllChecked"
|
|
||||||
can-move="ctrl.canMove"
|
|
||||||
can-delete="ctrl.canDelete"
|
|
||||||
move-to="ctrl.moveTo"
|
|
||||||
delete-item="ctrl.delete"
|
|
||||||
tag-filter-options="ctrl.tagFilterOptions"
|
|
||||||
selected-starred-filter="ctrl.selectedStarredFilter"
|
|
||||||
on-starred-filter-change="ctrl.onStarredFilterChange"
|
|
||||||
selected-tag-filter="ctrl.selectedTagFilter"
|
|
||||||
on-tagfilter-change="ctrl.onTagFilterChange"
|
|
||||||
/>
|
|
||||||
<div class="search-results-container">
|
|
||||||
<search-results
|
|
||||||
results="ctrl.sections"
|
|
||||||
editable="true"
|
|
||||||
on-selection-changed="ctrl.selectionChanged"
|
|
||||||
on-tag-selected="ctrl.filterByTag"
|
|
||||||
on-toggle-selection="ctrl.toggleSelection"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div ng-if="ctrl.canSave && ctrl.folderId && !ctrl.hasFilters && ctrl.sections.length === 0">
|
|
||||||
<empty-list-cta
|
|
||||||
title="'This folder doesn\'t have any dashboards yet'"
|
|
||||||
buttonIcon="'plus'"
|
|
||||||
buttonLink="'dashboard/new?folderId={{ctrl.folderId}}'"
|
|
||||||
buttonTitle="'Create Dashboard'"
|
|
||||||
proTip="'Add/move dashboards to your folder at ->'"
|
|
||||||
proTipLink="'dashboards'"
|
|
||||||
proTipLinkTitle="'Manage dashboards'"
|
|
||||||
proTipTarget=""
|
|
||||||
/>
|
|
||||||
</div>
|
|
@ -1,357 +0,0 @@
|
|||||||
import { IScope } from 'angular';
|
|
||||||
import _ from 'lodash';
|
|
||||||
import { SelectableValue } from '@grafana/data';
|
|
||||||
//@ts-ignore
|
|
||||||
import coreModule from 'app/core/core_module';
|
|
||||||
import appEvents from 'app/core/app_events';
|
|
||||||
import { SearchSrv } from 'app/core/services/search_srv';
|
|
||||||
import { backendSrv } from 'app/core/services/backend_srv';
|
|
||||||
import { ContextSrv } from 'app/core/services/context_srv';
|
|
||||||
import { CoreEvents } from 'app/types';
|
|
||||||
import { promiseToDigest } from '../../utils/promiseToDigest';
|
|
||||||
|
|
||||||
export interface Section {
|
|
||||||
id: number;
|
|
||||||
uid: string;
|
|
||||||
title: string;
|
|
||||||
expanded: boolean;
|
|
||||||
removable: boolean;
|
|
||||||
items: any[];
|
|
||||||
url: string;
|
|
||||||
icon: string;
|
|
||||||
score: number;
|
|
||||||
checked: boolean;
|
|
||||||
hideHeader: boolean;
|
|
||||||
toggle: Function;
|
|
||||||
type?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FoldersAndDashboardUids {
|
|
||||||
folderUids: string[];
|
|
||||||
dashboardUids: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
class Query {
|
|
||||||
query: string;
|
|
||||||
mode: string;
|
|
||||||
tag: any[];
|
|
||||||
starred: boolean;
|
|
||||||
skipRecent: boolean;
|
|
||||||
skipStarred: boolean;
|
|
||||||
folderIds: number[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ManageDashboardsCtrl {
|
|
||||||
sections: Section[];
|
|
||||||
|
|
||||||
query: Query;
|
|
||||||
navModel: any;
|
|
||||||
|
|
||||||
selectAllChecked = false;
|
|
||||||
|
|
||||||
// enable/disable actions depending on the folders or dashboards selected
|
|
||||||
canDelete = false;
|
|
||||||
canMove = false;
|
|
||||||
|
|
||||||
// filter variables
|
|
||||||
hasFilters = false;
|
|
||||||
tagFilterOptions: any[];
|
|
||||||
selectedTagFilter: any;
|
|
||||||
selectedStarredFilter: any;
|
|
||||||
|
|
||||||
// used when managing dashboards for a specific folder
|
|
||||||
folderId?: number;
|
|
||||||
folderUid?: string;
|
|
||||||
|
|
||||||
// if user can add new folders and/or add new dashboards
|
|
||||||
canSave = false;
|
|
||||||
|
|
||||||
// if user has editor role or higher
|
|
||||||
isEditor: boolean;
|
|
||||||
|
|
||||||
hasEditPermissionInFolders: boolean;
|
|
||||||
|
|
||||||
/** @ngInject */
|
|
||||||
constructor(private $scope: IScope, private searchSrv: SearchSrv, private contextSrv: ContextSrv) {
|
|
||||||
this.isEditor = this.contextSrv.isEditor;
|
|
||||||
this.hasEditPermissionInFolders = this.contextSrv.hasEditPermissionInFolders;
|
|
||||||
|
|
||||||
this.query = {
|
|
||||||
query: '',
|
|
||||||
mode: 'tree',
|
|
||||||
tag: [],
|
|
||||||
starred: false,
|
|
||||||
skipRecent: true,
|
|
||||||
skipStarred: true,
|
|
||||||
folderIds: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.folderId) {
|
|
||||||
this.query.folderIds = [this.folderId];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.refreshList().then(() => {
|
|
||||||
this.initTagFilter();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshList() {
|
|
||||||
return this.searchSrv
|
|
||||||
.search(this.query)
|
|
||||||
.then((result: Section[]) => {
|
|
||||||
return this.initDashboardList(result);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
if (!this.folderUid) {
|
|
||||||
this.$scope.$digest();
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return backendSrv.getFolderByUid(this.folderUid).then((folder: any) => {
|
|
||||||
this.canSave = folder.canSave;
|
|
||||||
if (!this.canSave) {
|
|
||||||
this.hasEditPermissionInFolders = false;
|
|
||||||
}
|
|
||||||
this.$scope.$digest();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
initDashboardList(result: Section[]) {
|
|
||||||
this.canMove = false;
|
|
||||||
this.canDelete = false;
|
|
||||||
this.selectAllChecked = false;
|
|
||||||
this.hasFilters = this.query.query.length > 0 || this.query.tag.length > 0 || this.query.starred;
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
this.sections = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sections = result;
|
|
||||||
|
|
||||||
for (const section of this.sections) {
|
|
||||||
section.checked = false;
|
|
||||||
|
|
||||||
for (const dashboard of section.items) {
|
|
||||||
dashboard.checked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.folderId && this.sections.length > 0) {
|
|
||||||
this.sections[0].hideHeader = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selectionChanged = () => {
|
|
||||||
let selectedDashboards = 0;
|
|
||||||
|
|
||||||
if (this.sections) {
|
|
||||||
for (const section of this.sections) {
|
|
||||||
selectedDashboards += _.filter(section.items, { checked: true } as any).length;
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectedFolders = _.filter(this.sections, { checked: true }).length;
|
|
||||||
this.canMove = selectedDashboards > 0;
|
|
||||||
this.canDelete = selectedDashboards > 0 || selectedFolders > 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
getFoldersAndDashboardsToDelete(): FoldersAndDashboardUids {
|
|
||||||
const selectedDashboards: FoldersAndDashboardUids = {
|
|
||||||
folderUids: [],
|
|
||||||
dashboardUids: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const section of this.sections) {
|
|
||||||
if (section.checked && section.id !== 0) {
|
|
||||||
selectedDashboards.folderUids.push(section.uid);
|
|
||||||
} else {
|
|
||||||
const selected = _.filter(section.items, { checked: true } as any);
|
|
||||||
selectedDashboards.dashboardUids.push(..._.map(selected, 'uid'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return selectedDashboards;
|
|
||||||
}
|
|
||||||
|
|
||||||
getFolderIds(sections: Section[]) {
|
|
||||||
const ids = [];
|
|
||||||
for (const s of sections) {
|
|
||||||
if (s.checked) {
|
|
||||||
ids.push(s.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete = () => {
|
|
||||||
const data = this.getFoldersAndDashboardsToDelete();
|
|
||||||
const folderCount = data.folderUids.length;
|
|
||||||
const dashCount = data.dashboardUids.length;
|
|
||||||
let text = 'Do you want to delete the ';
|
|
||||||
let text2;
|
|
||||||
|
|
||||||
if (folderCount > 0 && dashCount > 0) {
|
|
||||||
text += `selected folder${folderCount === 1 ? '' : 's'} and dashboard${dashCount === 1 ? '' : 's'}?`;
|
|
||||||
text2 = `All dashboards of the selected folder${folderCount === 1 ? '' : 's'} will also be deleted`;
|
|
||||||
} else if (folderCount > 0) {
|
|
||||||
text += `selected folder${folderCount === 1 ? '' : 's'} and all its dashboards?`;
|
|
||||||
} else {
|
|
||||||
text += `selected dashboard${dashCount === 1 ? '' : 's'}?`;
|
|
||||||
}
|
|
||||||
|
|
||||||
appEvents.emit(CoreEvents.showConfirmModal, {
|
|
||||||
title: 'Delete',
|
|
||||||
text: text,
|
|
||||||
text2: text2,
|
|
||||||
icon: 'trash-alt',
|
|
||||||
yesText: 'Delete',
|
|
||||||
onConfirm: () => {
|
|
||||||
this.deleteFoldersAndDashboards(data.folderUids, data.dashboardUids);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
private deleteFoldersAndDashboards(folderUids: string[], dashboardUids: string[]) {
|
|
||||||
promiseToDigest(this.$scope)(
|
|
||||||
backendSrv.deleteFoldersAndDashboards(folderUids, dashboardUids).then(() => {
|
|
||||||
this.refreshList();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getDashboardsToMove() {
|
|
||||||
const selectedDashboards = [];
|
|
||||||
|
|
||||||
for (const section of this.sections) {
|
|
||||||
const selected = _.filter(section.items, { checked: true } as any);
|
|
||||||
selectedDashboards.push(..._.map(selected, 'uid'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return selectedDashboards;
|
|
||||||
}
|
|
||||||
|
|
||||||
moveTo = () => {
|
|
||||||
const selectedDashboards = this.getDashboardsToMove();
|
|
||||||
|
|
||||||
const template =
|
|
||||||
'<move-to-folder-modal dismiss="dismiss()" ' +
|
|
||||||
'dashboards="model.dashboards" after-save="model.afterSave()">' +
|
|
||||||
'</move-to-folder-modal>';
|
|
||||||
appEvents.emit(CoreEvents.showModal, {
|
|
||||||
templateHtml: template,
|
|
||||||
modalClass: 'modal--narrow',
|
|
||||||
model: {
|
|
||||||
dashboards: selectedDashboards,
|
|
||||||
afterSave: this.refreshList.bind(this),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
initTagFilter() {
|
|
||||||
return this.searchSrv.getDashboardTags().then((results: any) => {
|
|
||||||
this.tagFilterOptions = results.map((result: any) => ({ value: result.term, label: result.term }));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
filterByTag = (tag: any) => {
|
|
||||||
if (tag) {
|
|
||||||
if (_.indexOf(this.query.tag, tag) === -1) {
|
|
||||||
this.query.tag.push(tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.refreshList();
|
|
||||||
};
|
|
||||||
|
|
||||||
onQueryChange() {
|
|
||||||
return this.refreshList();
|
|
||||||
}
|
|
||||||
|
|
||||||
onTagFilterChange = (filter: SelectableValue) => {
|
|
||||||
const res = this.filterByTag(filter.value);
|
|
||||||
this.selectedTagFilter = filter.value;
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
removeTag(tag: any, evt: Event) {
|
|
||||||
this.query.tag = _.without(this.query.tag, tag);
|
|
||||||
this.refreshList();
|
|
||||||
if (evt) {
|
|
||||||
evt.stopPropagation();
|
|
||||||
evt.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeStarred() {
|
|
||||||
this.query.starred = false;
|
|
||||||
return this.refreshList();
|
|
||||||
}
|
|
||||||
|
|
||||||
onStarredFilterChange = (filter: SelectableValue) => {
|
|
||||||
this.query.starred = filter.value;
|
|
||||||
this.selectedStarredFilter = filter.value;
|
|
||||||
return this.refreshList();
|
|
||||||
};
|
|
||||||
|
|
||||||
onSelectAllChanged = () => {
|
|
||||||
this.selectAllChecked = !this.selectAllChecked;
|
|
||||||
|
|
||||||
for (const section of this.sections) {
|
|
||||||
if (!section.hideHeader) {
|
|
||||||
section.checked = this.selectAllChecked;
|
|
||||||
}
|
|
||||||
|
|
||||||
section.items = _.map(section.items, (item: any) => {
|
|
||||||
item.checked = this.selectAllChecked;
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.selectionChanged();
|
|
||||||
};
|
|
||||||
|
|
||||||
clearFilters() {
|
|
||||||
this.query.query = '';
|
|
||||||
this.query.tag = [];
|
|
||||||
this.query.starred = false;
|
|
||||||
this.selectedStarredFilter = 'starred';
|
|
||||||
this.selectedTagFilter = 'tag';
|
|
||||||
this.refreshList();
|
|
||||||
}
|
|
||||||
|
|
||||||
createDashboardUrl() {
|
|
||||||
let url = 'dashboard/new';
|
|
||||||
|
|
||||||
if (this.folderId) {
|
|
||||||
url += `?folderId=${this.folderId}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
importDashboardUrl() {
|
|
||||||
let url = 'dashboard/import';
|
|
||||||
|
|
||||||
if (this.folderId) {
|
|
||||||
url += `?folderId=${this.folderId}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function manageDashboardsDirective() {
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
templateUrl: 'public/app/core/components/manage_dashboards/manage_dashboards.html',
|
|
||||||
controller: ManageDashboardsCtrl,
|
|
||||||
bindToController: true,
|
|
||||||
controllerAs: 'ctrl',
|
|
||||||
scope: {
|
|
||||||
folderId: '=',
|
|
||||||
folderUid: '=',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//coreModule.directive('manageDashboards', manageDashboardsDirective);
|
|
@ -1,62 +0,0 @@
|
|||||||
<div class="search-backdrop" ng-if="ctrl.isOpen"></div>
|
|
||||||
|
|
||||||
<div class="search-container" ng-if="ctrl.isOpen">
|
|
||||||
<search-field
|
|
||||||
query="ctrl.query"
|
|
||||||
autoFocus="ctrl.giveSearchFocus"
|
|
||||||
on-change="ctrl.onQueryChange"
|
|
||||||
on-key-down="ctrl.onKeyDown"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="search-dropdown">
|
|
||||||
<div class="search-dropdown__col_1">
|
|
||||||
<div class="search-results-scroller">
|
|
||||||
<div class="search-results-container" grafana-scrollbar>
|
|
||||||
<search-results
|
|
||||||
results="ctrl.results"
|
|
||||||
on-tag-selected="ctrl.filterByTag"
|
|
||||||
on-folder-expanding="ctrl.folderExpanding"
|
|
||||||
on-selection-changed="ctrl.selectionChanged"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="search-dropdown__col_2">
|
|
||||||
<div class="search-filter-box" ng-click="ctrl.onFilterboxClick()">
|
|
||||||
<div class="search-filter-box__header">
|
|
||||||
<icon name="'filter'"></icon>
|
|
||||||
Filter by:
|
|
||||||
<a class="pointer pull-right small" ng-click="ctrl.clearSearchFilter()">
|
|
||||||
<icon name="'times'" size="'sm'"></icon> Clear
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<tag-filter tags="ctrl.query.tags" tagOptions="ctrl.getTags" on-change="ctrl.onTagFiltersChanged"> </tag-filter>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="search-filter-box" ng-if="ctrl.isEditor || ctrl.hasEditPermissionInFolders">
|
|
||||||
<a href="dashboard/new" class="search-filter-box-link">
|
|
||||||
<icon name="'plus-square'" size="'xl'" style="margin-right: 8px;"></icon> New dashboard
|
|
||||||
</a>
|
|
||||||
<a href="dashboards/folder/new" class="search-filter-box-link" ng-if="ctrl.isEditor">
|
|
||||||
<icon name="'folder-plus'" size="'xl'" style="margin-right: 8px;"></icon> New folder
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="dashboard/import"
|
|
||||||
class="search-filter-box-link"
|
|
||||||
ng-if="ctrl.isEditor || ctrl.hasEditPermissionInFolders"
|
|
||||||
>
|
|
||||||
<icon name="'import'" size="'xl'" style="margin-right: 8px;"></icon> Import dashboard
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
class="search-filter-box-link"
|
|
||||||
target="_blank"
|
|
||||||
href="https://grafana.com/dashboards?utm_source=grafana_search"
|
|
||||||
>
|
|
||||||
<icon name="'apps'" size="'xl'" style="margin-right: 8px;"></icon> Find dashboards on Grafana.com
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,355 +0,0 @@
|
|||||||
import _, { debounce } from 'lodash';
|
|
||||||
import coreModule from '../../core_module';
|
|
||||||
import { SearchSrv } from 'app/core/services/search_srv';
|
|
||||||
import { contextSrv } from 'app/core/services/context_srv';
|
|
||||||
|
|
||||||
import appEvents from 'app/core/app_events';
|
|
||||||
import { parse, SearchParserOptions, SearchParserResult } from 'search-query-parser';
|
|
||||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
|
||||||
import { CoreEvents } from 'app/types';
|
|
||||||
|
|
||||||
export interface SearchQuery {
|
|
||||||
query: string;
|
|
||||||
parsedQuery: SearchParserResult;
|
|
||||||
tags: string[];
|
|
||||||
starred: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
class SearchQueryParser {
|
|
||||||
config: SearchParserOptions;
|
|
||||||
constructor(config: SearchParserOptions) {
|
|
||||||
this.config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
parse(query: string) {
|
|
||||||
const parsedQuery = parse(query, this.config);
|
|
||||||
|
|
||||||
if (typeof parsedQuery === 'string') {
|
|
||||||
return {
|
|
||||||
text: parsedQuery,
|
|
||||||
} as SearchParserResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parsedQuery;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SelectedIndicies {
|
|
||||||
dashboardIndex?: number;
|
|
||||||
folderIndex?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OpenSearchParams {
|
|
||||||
query?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SearchCtrl {
|
|
||||||
isOpen: boolean;
|
|
||||||
query: SearchQuery;
|
|
||||||
giveSearchFocus: boolean;
|
|
||||||
selectedIndex: number;
|
|
||||||
results: any;
|
|
||||||
currentSearchId: number;
|
|
||||||
showImport: boolean;
|
|
||||||
dismiss: any;
|
|
||||||
ignoreClose: any;
|
|
||||||
isLoading: boolean;
|
|
||||||
initialFolderFilterTitle: string;
|
|
||||||
isEditor: string;
|
|
||||||
hasEditPermissionInFolders: boolean;
|
|
||||||
queryParser: SearchQueryParser;
|
|
||||||
|
|
||||||
/** @ngInject */
|
|
||||||
constructor(private $scope: any, private $location: any, private $timeout: any, private searchSrv: SearchSrv) {
|
|
||||||
appEvents.on(CoreEvents.showDashSearch, this.openSearch.bind(this), $scope);
|
|
||||||
appEvents.on(CoreEvents.hideDashSearch, this.closeSearch.bind(this), $scope);
|
|
||||||
appEvents.on(CoreEvents.searchQuery, debounce(this.search.bind(this), 500), $scope);
|
|
||||||
|
|
||||||
this.initialFolderFilterTitle = 'All';
|
|
||||||
this.isEditor = contextSrv.isEditor;
|
|
||||||
this.hasEditPermissionInFolders = contextSrv.hasEditPermissionInFolders;
|
|
||||||
this.onQueryChange = this.onQueryChange.bind(this);
|
|
||||||
this.onKeyDown = this.onKeyDown.bind(this);
|
|
||||||
this.query = {
|
|
||||||
query: '',
|
|
||||||
parsedQuery: { text: '' },
|
|
||||||
tags: [],
|
|
||||||
starred: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.queryParser = new SearchQueryParser({
|
|
||||||
keywords: ['folder'],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
closeSearch() {
|
|
||||||
this.isOpen = this.ignoreClose;
|
|
||||||
}
|
|
||||||
|
|
||||||
onQueryChange(query: SearchQuery | string) {
|
|
||||||
if (typeof query === 'string') {
|
|
||||||
this.query = {
|
|
||||||
...this.query,
|
|
||||||
parsedQuery: this.queryParser.parse(query),
|
|
||||||
query: query,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
this.query = query;
|
|
||||||
}
|
|
||||||
appEvents.emit(CoreEvents.searchQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
openSearch(payload: OpenSearchParams = {}) {
|
|
||||||
if (this.isOpen) {
|
|
||||||
this.closeSearch();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isOpen = true;
|
|
||||||
this.giveSearchFocus = true;
|
|
||||||
this.selectedIndex = -1;
|
|
||||||
this.results = [];
|
|
||||||
this.query = {
|
|
||||||
query: payload.query ? `${payload.query} ` : '',
|
|
||||||
parsedQuery: this.queryParser.parse(payload.query),
|
|
||||||
tags: [],
|
|
||||||
starred: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.currentSearchId = 0;
|
|
||||||
this.ignoreClose = true;
|
|
||||||
this.isLoading = true;
|
|
||||||
|
|
||||||
this.$timeout(() => {
|
|
||||||
this.ignoreClose = false;
|
|
||||||
this.giveSearchFocus = true;
|
|
||||||
this.search();
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
onKeyDown(evt: KeyboardEvent) {
|
|
||||||
if (evt.keyCode === 27) {
|
|
||||||
this.closeSearch();
|
|
||||||
}
|
|
||||||
if (evt.keyCode === 40) {
|
|
||||||
this.moveSelection(1);
|
|
||||||
}
|
|
||||||
if (evt.keyCode === 38) {
|
|
||||||
this.moveSelection(-1);
|
|
||||||
}
|
|
||||||
if (evt.keyCode === 13) {
|
|
||||||
const flattenedResult = this.getFlattenedResultForNavigation();
|
|
||||||
const currentItem = flattenedResult[this.selectedIndex];
|
|
||||||
|
|
||||||
if (currentItem) {
|
|
||||||
if (currentItem.dashboardIndex !== undefined) {
|
|
||||||
const selectedDash = this.results[currentItem.folderIndex].items[currentItem.dashboardIndex];
|
|
||||||
|
|
||||||
if (selectedDash) {
|
|
||||||
this.$location.search({});
|
|
||||||
this.$location.path(selectedDash.url);
|
|
||||||
this.closeSearch();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const selectedFolder = this.results[currentItem.folderIndex];
|
|
||||||
|
|
||||||
if (selectedFolder) {
|
|
||||||
selectedFolder.toggle(selectedFolder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onFilterboxClick() {
|
|
||||||
this.giveSearchFocus = false;
|
|
||||||
this.preventClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
preventClose() {
|
|
||||||
this.ignoreClose = true;
|
|
||||||
|
|
||||||
this.$timeout(() => {
|
|
||||||
this.ignoreClose = false;
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
moveSelection(direction: number) {
|
|
||||||
if (this.results.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const flattenedResult = this.getFlattenedResultForNavigation();
|
|
||||||
const currentItem = flattenedResult[this.selectedIndex];
|
|
||||||
|
|
||||||
if (currentItem) {
|
|
||||||
if (currentItem.dashboardIndex !== undefined) {
|
|
||||||
this.results[currentItem.folderIndex].items[currentItem.dashboardIndex].selected = false;
|
|
||||||
} else {
|
|
||||||
this.results[currentItem.folderIndex].selected = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (direction === 0) {
|
|
||||||
this.selectedIndex = -1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const max = flattenedResult.length;
|
|
||||||
const newIndex = (this.selectedIndex + direction) % max;
|
|
||||||
this.selectedIndex = newIndex < 0 ? newIndex + max : newIndex;
|
|
||||||
const selectedItem = flattenedResult[this.selectedIndex];
|
|
||||||
|
|
||||||
if (selectedItem.dashboardIndex === undefined && this.results[selectedItem.folderIndex].id === 0) {
|
|
||||||
this.moveSelection(direction);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedItem.dashboardIndex !== undefined) {
|
|
||||||
if (!this.results[selectedItem.folderIndex].expanded) {
|
|
||||||
this.moveSelection(direction);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.results[selectedItem.folderIndex].items[selectedItem.dashboardIndex].selected = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.results[selectedItem.folderIndex].hideHeader) {
|
|
||||||
this.moveSelection(direction);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.results[selectedItem.folderIndex].selected = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
searchDashboards(folderContext?: string) {
|
|
||||||
this.currentSearchId = this.currentSearchId + 1;
|
|
||||||
const localSearchId = this.currentSearchId;
|
|
||||||
const folderIds = [];
|
|
||||||
|
|
||||||
const { parsedQuery } = this.query;
|
|
||||||
|
|
||||||
if (folderContext === 'current') {
|
|
||||||
folderIds.push(getDashboardSrv().getCurrent().meta.folderId);
|
|
||||||
}
|
|
||||||
|
|
||||||
const query = {
|
|
||||||
...this.query,
|
|
||||||
query: parsedQuery.text,
|
|
||||||
tag: this.query.tags,
|
|
||||||
folderIds,
|
|
||||||
};
|
|
||||||
|
|
||||||
return this.searchSrv
|
|
||||||
.search({
|
|
||||||
...query,
|
|
||||||
})
|
|
||||||
.then(results => {
|
|
||||||
if (localSearchId < this.currentSearchId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.results = results || [];
|
|
||||||
this.isLoading = false;
|
|
||||||
this.moveSelection(1);
|
|
||||||
this.$scope.$digest();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
queryHasNoFilters() {
|
|
||||||
const query = this.query;
|
|
||||||
return query.query === '' && query.starred === false && query.tags.length === 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
filterByTag = (tag: string) => {
|
|
||||||
if (tag) {
|
|
||||||
if (_.indexOf(this.query.tags, tag) === -1) {
|
|
||||||
this.query.tags.push(tag);
|
|
||||||
this.search();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
removeTag(tag: string, evt: any) {
|
|
||||||
this.query.tags = _.without(this.query.tags, tag);
|
|
||||||
this.search();
|
|
||||||
this.giveSearchFocus = true;
|
|
||||||
evt.stopPropagation();
|
|
||||||
evt.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
getTags = () => {
|
|
||||||
return this.searchSrv.getDashboardTags();
|
|
||||||
};
|
|
||||||
|
|
||||||
onTagFiltersChanged = (tags: string[]) => {
|
|
||||||
this.query.tags = tags;
|
|
||||||
this.search();
|
|
||||||
};
|
|
||||||
|
|
||||||
clearSearchFilter() {
|
|
||||||
this.query.query = '';
|
|
||||||
this.query.tags = [];
|
|
||||||
this.search();
|
|
||||||
}
|
|
||||||
|
|
||||||
showStarred() {
|
|
||||||
this.query.starred = !this.query.starred;
|
|
||||||
this.giveSearchFocus = true;
|
|
||||||
this.search();
|
|
||||||
}
|
|
||||||
|
|
||||||
selectionChanged = () => {
|
|
||||||
// TODO remove after React-side state management is implemented
|
|
||||||
// This method is only used as a callback after toggling section, to trigger results rerender
|
|
||||||
};
|
|
||||||
|
|
||||||
search() {
|
|
||||||
this.showImport = false;
|
|
||||||
this.selectedIndex = -1;
|
|
||||||
this.searchDashboards(this.query.parsedQuery['folder']);
|
|
||||||
}
|
|
||||||
|
|
||||||
folderExpanding = () => {
|
|
||||||
this.moveSelection(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
private getFlattenedResultForNavigation(): SelectedIndicies[] {
|
|
||||||
let folderIndex = 0;
|
|
||||||
|
|
||||||
return _.flatMap(this.results, (s: any) => {
|
|
||||||
let result: SelectedIndicies[] = [];
|
|
||||||
|
|
||||||
result.push({
|
|
||||||
folderIndex: folderIndex,
|
|
||||||
});
|
|
||||||
|
|
||||||
let dashboardIndex = 0;
|
|
||||||
|
|
||||||
result = result.concat(
|
|
||||||
_.map(s.items || [], i => {
|
|
||||||
return {
|
|
||||||
folderIndex: folderIndex,
|
|
||||||
dashboardIndex: dashboardIndex++,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
folderIndex++;
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function searchDirective() {
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
templateUrl: 'public/app/core/components/search/search.html',
|
|
||||||
controller: SearchCtrl,
|
|
||||||
bindToController: true,
|
|
||||||
controllerAs: 'ctrl',
|
|
||||||
scope: {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
coreModule.directive('dashboardSearch', searchDirective);
|
|
@ -1,64 +0,0 @@
|
|||||||
<div ng-repeat="section in ctrl.results" class="search-section">
|
|
||||||
<div
|
|
||||||
class="search-section__header pointer"
|
|
||||||
ng-hide="section.hideHeader"
|
|
||||||
ng-class="{'selected': section.selected}"
|
|
||||||
ng-click="ctrl.toggleFolderExpand(section)"
|
|
||||||
>
|
|
||||||
<div ng-click="ctrl.toggleSelection(section, $event)" class="center-vh">
|
|
||||||
<gf-form-checkbox
|
|
||||||
ng-show="ctrl.editable"
|
|
||||||
on-change="ctrl.selectionChanged($event)"
|
|
||||||
checked="section.checked"
|
|
||||||
switch-class="gf-form-checkbox--transparent"
|
|
||||||
>
|
|
||||||
</gf-form-checkbox>
|
|
||||||
</div>
|
|
||||||
<icon class="search-section__header__icon" ng-class="section.icon"></icon>
|
|
||||||
<span class="search-section__header__text">{{::section.title}}</span>
|
|
||||||
<a ng-show="section.url" href="{{section.url}}" class="search-section__header__link">
|
|
||||||
<icon name="'cog'"></icon>
|
|
||||||
</a>
|
|
||||||
<icon class="search-section__header__toggle" name="'angle-down'" ng-show="section.expanded"></icon>
|
|
||||||
<icon class="search-section__header__toggle" name="'angle-right'" ng-hide="section.expanded"></icon>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="search-section__header" ng-show="section.hideHeader"></div>
|
|
||||||
|
|
||||||
<div ng-if="section.expanded">
|
|
||||||
<a
|
|
||||||
ng-repeat="item in section.items"
|
|
||||||
class="search-item search-item--indent"
|
|
||||||
ng-class="{'selected': item.selected}"
|
|
||||||
ng-href="{{::item.url}}"
|
|
||||||
aria-label="{{ctrl.selectors.dashboards(item.title)}}"
|
|
||||||
>
|
|
||||||
<div ng-click="ctrl.toggleSelection(item, $event)" class="center-vh">
|
|
||||||
<gf-form-checkbox
|
|
||||||
ng-show="ctrl.editable"
|
|
||||||
on-change="ctrl.selectionChanged()"
|
|
||||||
checked="item.checked"
|
|
||||||
switch-class="gf-form-checkbox--transparent"
|
|
||||||
>
|
|
||||||
</gf-form-checkbox>
|
|
||||||
</div>
|
|
||||||
<span class="search-item__icon">
|
|
||||||
<i class="gicon mini gicon-dashboard-list"></i>
|
|
||||||
</span>
|
|
||||||
<span class="search-item__body" ng-click="ctrl.onItemClick(item)">
|
|
||||||
<div class="search-item__body-title">{{::item.title}}</div>
|
|
||||||
<span class="search-item__body-folder-title">{{::item.folderTitle}}</span>
|
|
||||||
</span>
|
|
||||||
<span class="search-item__tags">
|
|
||||||
<span
|
|
||||||
ng-click="ctrl.selectTag(tag, $event)"
|
|
||||||
ng-repeat="tag in item.tags"
|
|
||||||
tag-color-from-name="tag"
|
|
||||||
class="label label-tag"
|
|
||||||
>
|
|
||||||
{{tag}}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,102 +0,0 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
import { ILocationService, IScope } from 'angular';
|
|
||||||
import { e2e } from '@grafana/e2e';
|
|
||||||
|
|
||||||
import coreModule from '../../core_module';
|
|
||||||
import appEvents from 'app/core/app_events';
|
|
||||||
import { CoreEvents } from 'app/types';
|
|
||||||
import { promiseToDigest } from '../../utils/promiseToDigest';
|
|
||||||
|
|
||||||
export class SearchResultsCtrl {
|
|
||||||
results: any;
|
|
||||||
onSelectionChanged: any;
|
|
||||||
onTagSelected: any;
|
|
||||||
onFolderExpanding: any;
|
|
||||||
editable: boolean;
|
|
||||||
selectors: typeof e2e.pages.Dashboards.selectors;
|
|
||||||
|
|
||||||
/** @ngInject */
|
|
||||||
constructor(private $location: ILocationService, private $scope: IScope) {
|
|
||||||
this.selectors = e2e.pages.Dashboards.selectors;
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleFolderExpand(section: any) {
|
|
||||||
if (section.toggle) {
|
|
||||||
if (!section.expanded && this.onFolderExpanding) {
|
|
||||||
this.onFolderExpanding();
|
|
||||||
}
|
|
||||||
|
|
||||||
promiseToDigest(this.$scope)(
|
|
||||||
section.toggle(section).then((f: any) => {
|
|
||||||
if (this.editable && f.expanded) {
|
|
||||||
if (f.items) {
|
|
||||||
_.each(f.items, i => {
|
|
||||||
i.checked = f.checked;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.onSelectionChanged) {
|
|
||||||
this.onSelectionChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleSelection(item: any, evt: any) {
|
|
||||||
item.checked = !item.checked;
|
|
||||||
|
|
||||||
if (item.items) {
|
|
||||||
_.each(item.items, i => {
|
|
||||||
i.checked = item.checked;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.onSelectionChanged) {
|
|
||||||
this.onSelectionChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (evt) {
|
|
||||||
evt.stopPropagation();
|
|
||||||
evt.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onItemClick(item: any) {
|
|
||||||
//Check if one string can be found in the other
|
|
||||||
if (this.$location.path().indexOf(item.url) > -1 || item.url.indexOf(this.$location.path()) > -1) {
|
|
||||||
appEvents.emit(CoreEvents.hideDashSearch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selectTag(tag: any, evt: any) {
|
|
||||||
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: '&',
|
|
||||||
onFolderExpanding: '&',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
coreModule.directive('dashboardSearchResults', searchResultsDirective);
|
|
@ -18,7 +18,6 @@ import './services/search_srv';
|
|||||||
import './services/ng_react';
|
import './services/ng_react';
|
||||||
import { colors, JsonExplorer } from '@grafana/ui/';
|
import { colors, JsonExplorer } from '@grafana/ui/';
|
||||||
|
|
||||||
import { searchDirective } from './components/search/search';
|
|
||||||
import { infoPopover } from './components/info_popover';
|
import { infoPopover } from './components/info_popover';
|
||||||
import { arrayJoin } from './directives/array_join';
|
import { arrayJoin } from './directives/array_join';
|
||||||
import { liveSrv } from './live/live_srv';
|
import { liveSrv } from './live/live_srv';
|
||||||
@ -42,8 +41,6 @@ import { profiler } from './profiler';
|
|||||||
import { registerAngularDirectives } from './angular_wrappers';
|
import { registerAngularDirectives } from './angular_wrappers';
|
||||||
import { updateLegendValues } from './time_series2';
|
import { updateLegendValues } from './time_series2';
|
||||||
import TimeSeries from './time_series2';
|
import TimeSeries from './time_series2';
|
||||||
import { searchResultsDirective } from './components/search/search_results';
|
|
||||||
import { manageDashboardsDirective } from './components/manage_dashboards/manage_dashboards';
|
|
||||||
import { NavModel } from '@grafana/data';
|
import { NavModel } from '@grafana/data';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@ -51,7 +48,6 @@ export {
|
|||||||
registerAngularDirectives,
|
registerAngularDirectives,
|
||||||
arrayJoin,
|
arrayJoin,
|
||||||
coreModule,
|
coreModule,
|
||||||
searchDirective,
|
|
||||||
liveSrv,
|
liveSrv,
|
||||||
switchDirective,
|
switchDirective,
|
||||||
infoPopover,
|
infoPopover,
|
||||||
@ -69,8 +65,6 @@ export {
|
|||||||
NavModelSrv,
|
NavModelSrv,
|
||||||
NavModel,
|
NavModel,
|
||||||
geminiScrollbar,
|
geminiScrollbar,
|
||||||
manageDashboardsDirective,
|
|
||||||
TimeSeries,
|
TimeSeries,
|
||||||
updateLegendValues,
|
updateLegendValues,
|
||||||
searchResultsDirective,
|
|
||||||
};
|
};
|
||||||
|
@ -7,12 +7,12 @@ import { AppEvents } from '@grafana/data';
|
|||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
||||||
import { DashboardSearchHit } from 'app/types/search';
|
import { DataSourceResponse } from 'app/types/events';
|
||||||
|
import { DashboardSearchHit } from 'app/features/search/types';
|
||||||
import { CoreEvents, DashboardDTO, FolderInfo } from 'app/types';
|
import { CoreEvents, DashboardDTO, FolderInfo } from 'app/types';
|
||||||
import { ContextSrv, contextSrv } from './context_srv';
|
|
||||||
import { coreModule } from 'app/core/core_module';
|
import { coreModule } from 'app/core/core_module';
|
||||||
|
import { ContextSrv, contextSrv } from './context_srv';
|
||||||
import { Emitter } from '../utils/emitter';
|
import { Emitter } from '../utils/emitter';
|
||||||
import { DataSourceResponse } from '../../types/events';
|
|
||||||
import { parseInitFromOptions, parseUrlFromOptions } from '../utils/fetch';
|
import { parseInitFromOptions, parseUrlFromOptions } from '../utils/fetch';
|
||||||
|
|
||||||
export interface DatasourceRequestOptions {
|
export interface DatasourceRequestOptions {
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import coreModule from 'app/core/core_module';
|
|
||||||
import impressionSrv from 'app/core/services/impression_srv';
|
import impressionSrv from 'app/core/services/impression_srv';
|
||||||
import store from 'app/core/store';
|
import store from 'app/core/store';
|
||||||
import { contextSrv } from 'app/core/services/context_srv';
|
import { contextSrv } from 'app/core/services/context_srv';
|
||||||
|
import { hasFilters } from 'app/features/search/utils';
|
||||||
|
import { DashboardSection, DashboardSearchItemType, DashboardSearchHit } from 'app/features/search/types';
|
||||||
import { backendSrv } from './backend_srv';
|
import { backendSrv } from './backend_srv';
|
||||||
import { Section } from '../components/manage_dashboards/manage_dashboards';
|
|
||||||
import { DashboardSearchHit, DashboardSearchHitType } from 'app/types/search';
|
|
||||||
import { hasFilters } from '../../features/search/utils';
|
|
||||||
|
|
||||||
interface Sections {
|
interface Sections {
|
||||||
[key: string]: Partial<Section>;
|
[key: string]: Partial<DashboardSection>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SearchSrv {
|
export class SearchSrv {
|
||||||
@ -22,18 +20,16 @@ export class SearchSrv {
|
|||||||
this.starredIsOpen = store.getBool('search.sections.starred', true);
|
this.starredIsOpen = store.getBool('search.sections.starred', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getRecentDashboards(sections: Sections) {
|
private getRecentDashboards(sections: DashboardSection[] | any) {
|
||||||
return this.queryForRecentDashboards().then((result: any[]) => {
|
return this.queryForRecentDashboards().then((result: any[]) => {
|
||||||
if (result.length > 0) {
|
if (result.length > 0) {
|
||||||
sections['recent'] = {
|
sections['recent'] = {
|
||||||
title: 'Recent',
|
title: 'Recent',
|
||||||
icon: 'clock-nine',
|
icon: 'clock-nine',
|
||||||
score: -1,
|
score: -1,
|
||||||
removable: true,
|
|
||||||
expanded: this.recentIsOpen,
|
expanded: this.recentIsOpen,
|
||||||
toggle: this.toggleRecent.bind(this),
|
|
||||||
items: result,
|
items: result,
|
||||||
type: DashboardSearchHitType.DashHitFolder,
|
type: DashboardSearchItemType.DashFolder,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -50,45 +46,24 @@ export class SearchSrv {
|
|||||||
.map(orderId => {
|
.map(orderId => {
|
||||||
return _.find(result, { id: orderId });
|
return _.find(result, { id: orderId });
|
||||||
})
|
})
|
||||||
.filter(hit => hit && !hit.isStarred);
|
.filter(hit => hit && !hit.isStarred) as DashboardSearchHit[];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private toggleRecent(section: Section) {
|
private getStarred(sections: DashboardSection) {
|
||||||
this.recentIsOpen = section.expanded = !section.expanded;
|
|
||||||
store.set('search.sections.recent', this.recentIsOpen);
|
|
||||||
|
|
||||||
if (!section.expanded || section.items.length) {
|
|
||||||
return Promise.resolve(section);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.queryForRecentDashboards().then(result => {
|
|
||||||
section.items = result;
|
|
||||||
return Promise.resolve(section);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private toggleStarred(section: Section) {
|
|
||||||
this.starredIsOpen = section.expanded = !section.expanded;
|
|
||||||
store.set('search.sections.starred', this.starredIsOpen);
|
|
||||||
return Promise.resolve(section);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getStarred(sections: Sections) {
|
|
||||||
if (!contextSrv.isSignedIn) {
|
if (!contextSrv.isSignedIn) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
return backendSrv.search({ starred: true, limit: 30 }).then(result => {
|
return backendSrv.search({ starred: true, limit: 30 }).then(result => {
|
||||||
if (result.length > 0) {
|
if (result.length > 0) {
|
||||||
sections['starred'] = {
|
(sections as any)['starred'] = {
|
||||||
title: 'Starred',
|
title: 'Starred',
|
||||||
icon: 'star',
|
icon: 'star',
|
||||||
score: -2,
|
score: -2,
|
||||||
expanded: this.starredIsOpen,
|
expanded: this.starredIsOpen,
|
||||||
toggle: this.toggleStarred.bind(this),
|
|
||||||
items: result,
|
items: result,
|
||||||
type: DashboardSearchHitType.DashHitFolder,
|
type: DashboardSearchItemType.DashFolder,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -138,7 +113,6 @@ export class SearchSrv {
|
|||||||
title: hit.title,
|
title: hit.title,
|
||||||
expanded: false,
|
expanded: false,
|
||||||
items: [],
|
items: [],
|
||||||
toggle: this.toggleFolder.bind(this),
|
|
||||||
url: hit.url,
|
url: hit.url,
|
||||||
icon: 'folder',
|
icon: 'folder',
|
||||||
score: _.keys(sections).length,
|
score: _.keys(sections).length,
|
||||||
@ -162,9 +136,8 @@ export class SearchSrv {
|
|||||||
url: hit.folderUrl,
|
url: hit.folderUrl,
|
||||||
items: [],
|
items: [],
|
||||||
icon: 'folder-open',
|
icon: 'folder-open',
|
||||||
toggle: this.toggleFolder.bind(this),
|
|
||||||
score: _.keys(sections).length,
|
score: _.keys(sections).length,
|
||||||
type: DashboardSearchHitType.DashHitFolder,
|
type: DashboardSearchItemType.DashFolder,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
section = {
|
section = {
|
||||||
@ -172,9 +145,8 @@ export class SearchSrv {
|
|||||||
title: 'General',
|
title: 'General',
|
||||||
items: [],
|
items: [],
|
||||||
icon: 'folder-open',
|
icon: 'folder-open',
|
||||||
toggle: this.toggleFolder.bind(this),
|
|
||||||
score: _.keys(sections).length,
|
score: _.keys(sections).length,
|
||||||
type: DashboardSearchHitType.DashHitFolder,
|
type: DashboardSearchItemType.DashFolder,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// add section
|
// add section
|
||||||
@ -182,28 +154,10 @@ export class SearchSrv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
section.expanded = true;
|
section.expanded = true;
|
||||||
section.items.push(hit);
|
section.items && section.items.push(hit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private toggleFolder(section: Section) {
|
|
||||||
section.expanded = !section.expanded;
|
|
||||||
section.icon = section.expanded ? 'folder-open' : 'folder';
|
|
||||||
|
|
||||||
if (section.items.length) {
|
|
||||||
return Promise.resolve(section);
|
|
||||||
}
|
|
||||||
|
|
||||||
const query = {
|
|
||||||
folderIds: [section.id],
|
|
||||||
};
|
|
||||||
|
|
||||||
return backendSrv.search(query).then(results => {
|
|
||||||
section.items = results;
|
|
||||||
return Promise.resolve(section);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getDashboardTags() {
|
getDashboardTags() {
|
||||||
return backendSrv.get('/api/dashboards/tags');
|
return backendSrv.get('/api/dashboards/tags');
|
||||||
}
|
}
|
||||||
@ -212,5 +166,3 @@ export class SearchSrv {
|
|||||||
return backendSrv.get('/api/search/sorting');
|
return backendSrv.get('/api/search/sorting');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
coreModule.service('searchSrv', SearchSrv);
|
|
||||||
|
@ -1,598 +0,0 @@
|
|||||||
// @ts-ignore
|
|
||||||
import q from 'q';
|
|
||||||
import {
|
|
||||||
ManageDashboardsCtrl,
|
|
||||||
Section,
|
|
||||||
FoldersAndDashboardUids,
|
|
||||||
} from 'app/core/components/manage_dashboards/manage_dashboards';
|
|
||||||
import { SearchSrv } from 'app/core/services/search_srv';
|
|
||||||
import { ContextSrv } from '../services/context_srv';
|
|
||||||
|
|
||||||
const mockSection = (overides?: object): Section => {
|
|
||||||
const defaultSection: Section = {
|
|
||||||
id: 0,
|
|
||||||
items: [],
|
|
||||||
checked: false,
|
|
||||||
expanded: false,
|
|
||||||
removable: false,
|
|
||||||
hideHeader: false,
|
|
||||||
icon: '',
|
|
||||||
score: 0,
|
|
||||||
title: 'Some Section',
|
|
||||||
toggle: jest.fn(),
|
|
||||||
uid: 'someuid',
|
|
||||||
url: '/some/url/',
|
|
||||||
};
|
|
||||||
|
|
||||||
return { ...defaultSection, ...overides };
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('ManageDashboards', () => {
|
|
||||||
let ctrl: ManageDashboardsCtrl;
|
|
||||||
|
|
||||||
describe('when browsing dashboards', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
const tags: any[] = [];
|
|
||||||
const response = [
|
|
||||||
{
|
|
||||||
id: 410,
|
|
||||||
title: 'afolder',
|
|
||||||
type: 'dash-folder',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
id: 399,
|
|
||||||
title: 'Dashboard Test',
|
|
||||||
url: 'dashboard/db/dashboard-test',
|
|
||||||
icon: 'folder',
|
|
||||||
tags,
|
|
||||||
isStarred: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
tags,
|
|
||||||
isStarred: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
title: 'General',
|
|
||||||
icon: 'folder-open',
|
|
||||||
uri: 'db/something-else',
|
|
||||||
type: 'dash-db',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
id: 500,
|
|
||||||
title: 'Dashboard Test',
|
|
||||||
url: 'dashboard/db/dashboard-test',
|
|
||||||
icon: 'folder',
|
|
||||||
tags,
|
|
||||||
isStarred: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
tags,
|
|
||||||
isStarred: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
ctrl = createCtrlWithStubs(response);
|
|
||||||
return ctrl.refreshList();
|
|
||||||
});
|
|
||||||
|
|
||||||
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);
|
|
||||||
expect(ctrl.sections[0].hideHeader).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when browsing dashboards for a folder', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
const tags: any[] = [];
|
|
||||||
const response = [
|
|
||||||
{
|
|
||||||
id: 410,
|
|
||||||
title: 'afolder',
|
|
||||||
type: 'dash-folder',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
id: 399,
|
|
||||||
title: 'Dashboard Test',
|
|
||||||
url: 'dashboard/db/dashboard-test',
|
|
||||||
icon: 'folder',
|
|
||||||
tags,
|
|
||||||
isStarred: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
tags,
|
|
||||||
isStarred: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
ctrl = createCtrlWithStubs(response);
|
|
||||||
ctrl.folderId = 410;
|
|
||||||
return ctrl.refreshList();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set hide header to true on section', () => {
|
|
||||||
expect(ctrl.sections[0].hideHeader).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when searching dashboards', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
const tags: any[] = [];
|
|
||||||
const response = [
|
|
||||||
{
|
|
||||||
checked: false,
|
|
||||||
expanded: true,
|
|
||||||
hideHeader: true,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
id: 399,
|
|
||||||
title: 'Dashboard Test',
|
|
||||||
url: 'dashboard/db/dashboard-test',
|
|
||||||
icon: 'folder',
|
|
||||||
tags,
|
|
||||||
isStarred: false,
|
|
||||||
folderId: 410,
|
|
||||||
folderUid: 'uid',
|
|
||||||
folderTitle: 'Folder',
|
|
||||||
folderUrl: '/dashboards/f/uid/folder',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 500,
|
|
||||||
title: 'Dashboard Test',
|
|
||||||
url: 'dashboard/db/dashboard-test',
|
|
||||||
icon: 'folder',
|
|
||||||
tags,
|
|
||||||
folderId: 499,
|
|
||||||
isStarred: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
ctrl = createCtrlWithStubs(response);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('with query filter', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.query.query = 'd';
|
|
||||||
ctrl.canMove = true;
|
|
||||||
ctrl.canDelete = true;
|
|
||||||
ctrl.selectAllChecked = true;
|
|
||||||
return ctrl.refreshList();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set checked to false on all sections and children', () => {
|
|
||||||
expect(ctrl.sections.length).toEqual(1);
|
|
||||||
expect(ctrl.sections[0].checked).toEqual(false);
|
|
||||||
expect(ctrl.sections[0].items[0].checked).toEqual(false);
|
|
||||||
expect(ctrl.sections[0].items[1].checked).toEqual(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should uncheck select all', () => {
|
|
||||||
expect(ctrl.selectAllChecked).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should disable Move To button', () => {
|
|
||||||
expect(ctrl.canMove).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should disable delete button', () => {
|
|
||||||
expect(ctrl.canDelete).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have active filters', () => {
|
|
||||||
expect(ctrl.hasFilters).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when select all is checked', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectAllChecked = false;
|
|
||||||
ctrl.onSelectAllChanged();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select all dashboards', () => {
|
|
||||||
expect(ctrl.sections[0].checked).toBeFalsy();
|
|
||||||
expect(ctrl.sections[0].items[0].checked).toBeTruthy();
|
|
||||||
expect(ctrl.sections[0].items[1].checked).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should enable Move To button', () => {
|
|
||||||
expect(ctrl.canMove).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should enable delete button', () => {
|
|
||||||
expect(ctrl.canDelete).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when clearing filters', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
return ctrl.clearFilters();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reset query filter', () => {
|
|
||||||
expect(ctrl.query.query).toEqual('');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('with tag filter', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
return ctrl.filterByTag('test');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set tag filter', () => {
|
|
||||||
expect(ctrl.sections.length).toEqual(1);
|
|
||||||
expect(ctrl.query.tag[0]).toEqual('test');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have active filters', () => {
|
|
||||||
expect(ctrl.hasFilters).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when clearing filters', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
return ctrl.clearFilters();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reset tag filter', () => {
|
|
||||||
expect(ctrl.query.tag.length).toEqual(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('with starred filter', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
const yesOption: any = { label: 'Yes', value: true };
|
|
||||||
|
|
||||||
ctrl.selectedStarredFilter = yesOption;
|
|
||||||
return ctrl.onStarredFilterChange(yesOption);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set starred filter', () => {
|
|
||||||
expect(ctrl.sections.length).toEqual(1);
|
|
||||||
expect(ctrl.query.starred).toEqual(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have active filters', () => {
|
|
||||||
expect(ctrl.hasFilters).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when clearing filters', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
return ctrl.clearFilters();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reset starred filter', () => {
|
|
||||||
expect(ctrl.query.starred).toEqual(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when selecting dashboards', () => {
|
|
||||||
let ctrl: ManageDashboardsCtrl;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl = createCtrlWithStubs([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('and no dashboards are selected', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.sections = [
|
|
||||||
mockSection({
|
|
||||||
id: 1,
|
|
||||||
items: [{ id: 2, checked: false }],
|
|
||||||
checked: false,
|
|
||||||
}),
|
|
||||||
mockSection({
|
|
||||||
id: 0,
|
|
||||||
items: [{ id: 3, checked: false }],
|
|
||||||
checked: false,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
ctrl.selectionChanged();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should disable Move To button', () => {
|
|
||||||
expect(ctrl.canMove).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should disable delete button', () => {
|
|
||||||
expect(ctrl.canDelete).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when select all is checked', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectAllChecked = false;
|
|
||||||
ctrl.onSelectAllChanged();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select all folders and dashboards', () => {
|
|
||||||
expect(ctrl.sections[0].checked).toBeTruthy();
|
|
||||||
expect(ctrl.sections[0].items[0].checked).toBeTruthy();
|
|
||||||
expect(ctrl.sections[1].checked).toBeTruthy();
|
|
||||||
expect(ctrl.sections[1].items[0].checked).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should enable Move To button', () => {
|
|
||||||
expect(ctrl.canMove).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should enable delete button', () => {
|
|
||||||
expect(ctrl.canDelete).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('and all folders and dashboards are selected', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.sections = [
|
|
||||||
mockSection({
|
|
||||||
id: 1,
|
|
||||||
items: [{ id: 2, checked: true }],
|
|
||||||
checked: true,
|
|
||||||
}),
|
|
||||||
mockSection({
|
|
||||||
id: 0,
|
|
||||||
items: [{ id: 3, checked: true }],
|
|
||||||
checked: true,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
ctrl.selectionChanged();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should enable Move To button', () => {
|
|
||||||
expect(ctrl.canMove).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should enable delete button', () => {
|
|
||||||
expect(ctrl.canDelete).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when select all is unchecked', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectAllChecked = true;
|
|
||||||
ctrl.onSelectAllChanged();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should uncheck all checked folders and dashboards', () => {
|
|
||||||
expect(ctrl.sections[0].checked).toBeFalsy();
|
|
||||||
expect(ctrl.sections[0].items[0].checked).toBeFalsy();
|
|
||||||
expect(ctrl.sections[1].checked).toBeFalsy();
|
|
||||||
expect(ctrl.sections[1].items[0].checked).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should disable Move To button', () => {
|
|
||||||
expect(ctrl.canMove).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should disable delete button', () => {
|
|
||||||
expect(ctrl.canDelete).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('and one dashboard in root is selected', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.sections = [
|
|
||||||
mockSection({
|
|
||||||
id: 1,
|
|
||||||
title: 'folder',
|
|
||||||
items: [{ id: 2, checked: false }],
|
|
||||||
checked: false,
|
|
||||||
}),
|
|
||||||
mockSection({
|
|
||||||
id: 0,
|
|
||||||
title: 'General',
|
|
||||||
items: [{ id: 3, checked: true }],
|
|
||||||
checked: false,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
ctrl.selectionChanged();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should enable Move To button', () => {
|
|
||||||
expect(ctrl.canMove).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should enable delete button', () => {
|
|
||||||
expect(ctrl.canDelete).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('and one child dashboard is selected', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.sections = [
|
|
||||||
mockSection({
|
|
||||||
id: 1,
|
|
||||||
title: 'folder',
|
|
||||||
items: [{ id: 2, checked: true }],
|
|
||||||
checked: false,
|
|
||||||
}),
|
|
||||||
mockSection({
|
|
||||||
id: 0,
|
|
||||||
title: 'General',
|
|
||||||
items: [{ id: 3, checked: false }],
|
|
||||||
checked: false,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
ctrl.selectionChanged();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should enable Move To button', () => {
|
|
||||||
expect(ctrl.canMove).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should enable delete button', () => {
|
|
||||||
expect(ctrl.canDelete).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('and one child dashboard and one dashboard is selected', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.sections = [
|
|
||||||
mockSection({
|
|
||||||
id: 1,
|
|
||||||
title: 'folder',
|
|
||||||
items: [{ id: 2, checked: true }],
|
|
||||||
checked: false,
|
|
||||||
}),
|
|
||||||
mockSection({
|
|
||||||
id: 0,
|
|
||||||
title: 'General',
|
|
||||||
items: [{ id: 3, checked: true }],
|
|
||||||
checked: false,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
ctrl.selectionChanged();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should enable Move To button', () => {
|
|
||||||
expect(ctrl.canMove).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should enable delete button', () => {
|
|
||||||
expect(ctrl.canDelete).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('and one child dashboard and one folder is selected', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.sections = [
|
|
||||||
mockSection({
|
|
||||||
id: 1,
|
|
||||||
title: 'folder',
|
|
||||||
items: [{ id: 2, checked: false }],
|
|
||||||
checked: true,
|
|
||||||
}),
|
|
||||||
mockSection({
|
|
||||||
id: 3,
|
|
||||||
title: 'folder',
|
|
||||||
items: [{ id: 4, checked: true }],
|
|
||||||
checked: false,
|
|
||||||
}),
|
|
||||||
mockSection({
|
|
||||||
id: 0,
|
|
||||||
title: 'General',
|
|
||||||
items: [{ id: 3, checked: false }],
|
|
||||||
checked: false,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
ctrl.selectionChanged();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should enable Move To button', () => {
|
|
||||||
expect(ctrl.canMove).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should enable delete button', () => {
|
|
||||||
expect(ctrl.canDelete).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when deleting dashboards', () => {
|
|
||||||
let toBeDeleted: FoldersAndDashboardUids;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl = createCtrlWithStubs([]);
|
|
||||||
|
|
||||||
ctrl.sections = [
|
|
||||||
mockSection({
|
|
||||||
id: 1,
|
|
||||||
uid: 'folder',
|
|
||||||
title: 'folder',
|
|
||||||
items: [{ id: 2, checked: true, uid: 'folder-dash' }],
|
|
||||||
checked: true,
|
|
||||||
}),
|
|
||||||
mockSection({
|
|
||||||
id: 3,
|
|
||||||
title: 'folder-2',
|
|
||||||
items: [{ id: 3, checked: true, uid: 'folder-2-dash' }],
|
|
||||||
checked: false,
|
|
||||||
uid: 'folder-2',
|
|
||||||
}),
|
|
||||||
mockSection({
|
|
||||||
id: 0,
|
|
||||||
title: 'General',
|
|
||||||
items: [{ id: 3, checked: true, uid: 'root-dash' }],
|
|
||||||
checked: true,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
toBeDeleted = ctrl.getFoldersAndDashboardsToDelete();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return 1 folder', () => {
|
|
||||||
expect(toBeDeleted.folderUids.length).toEqual(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return 2 dashboards', () => {
|
|
||||||
expect(toBeDeleted.dashboardUids.length).toEqual(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should filter out children if parent is checked', () => {
|
|
||||||
expect(toBeDeleted.folderUids[0]).toEqual('folder');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not filter out children if parent not is checked', () => {
|
|
||||||
expect(toBeDeleted.dashboardUids[0]).toEqual('folder-2-dash');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not filter out children if parent is checked and root', () => {
|
|
||||||
expect(toBeDeleted.dashboardUids[1]).toEqual('root-dash');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when moving dashboards', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl = createCtrlWithStubs([]);
|
|
||||||
|
|
||||||
ctrl.sections = [
|
|
||||||
mockSection({
|
|
||||||
id: 1,
|
|
||||||
title: 'folder',
|
|
||||||
items: [{ id: 2, checked: true, uid: 'dash' }],
|
|
||||||
checked: false,
|
|
||||||
uid: 'folder',
|
|
||||||
}),
|
|
||||||
mockSection({
|
|
||||||
id: 0,
|
|
||||||
title: 'General',
|
|
||||||
items: [{ id: 3, checked: true, uid: '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(searchResponse: any, tags?: any) {
|
|
||||||
const searchSrvStub = {
|
|
||||||
search: (options: any) => {
|
|
||||||
return q.resolve(searchResponse);
|
|
||||||
},
|
|
||||||
getDashboardTags: () => {
|
|
||||||
return q.resolve(tags || []);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return new ManageDashboardsCtrl(
|
|
||||||
{ $digest: jest.fn() } as any,
|
|
||||||
searchSrvStub as SearchSrv,
|
|
||||||
{ isEditor: true } as ContextSrv
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,341 +0,0 @@
|
|||||||
import { SearchCtrl } from '../components/search/search';
|
|
||||||
import { SearchSrv } from '../services/search_srv';
|
|
||||||
|
|
||||||
jest.mock('app/core/services/context_srv', () => ({
|
|
||||||
contextSrv: {
|
|
||||||
user: { orgId: 1 },
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('SearchCtrl', () => {
|
|
||||||
const searchSrvStub = {
|
|
||||||
search: (options: any) => {},
|
|
||||||
getDashboardTags: () => {},
|
|
||||||
};
|
|
||||||
const ctrl = new SearchCtrl({ $on: () => {} }, {}, {}, searchSrvStub as SearchSrv);
|
|
||||||
|
|
||||||
describe('Given an empty result', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.results = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When navigating down one step', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectedIndex = 0;
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not navigate', () => {
|
|
||||||
expect(ctrl.selectedIndex).toBe(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When navigating up one step', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectedIndex = 0;
|
|
||||||
ctrl.moveSelection(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not navigate', () => {
|
|
||||||
expect(ctrl.selectedIndex).toBe(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Given a result of one selected collapsed folder with no dashboards and a root folder with 2 dashboards', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.results = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
title: 'folder',
|
|
||||||
items: [],
|
|
||||||
selected: true,
|
|
||||||
expanded: false,
|
|
||||||
toggle: (i: any) => (i.expanded = !i.expanded),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
title: 'General',
|
|
||||||
items: [
|
|
||||||
{ id: 3, selected: false },
|
|
||||||
{ id: 5, selected: false },
|
|
||||||
],
|
|
||||||
selected: false,
|
|
||||||
expanded: true,
|
|
||||||
toggle: (i: any) => (i.expanded = !i.expanded),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When navigating down one step', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectedIndex = 0;
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select first dashboard in root folder', () => {
|
|
||||||
expect(ctrl.results[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].items[0].selected).toBeTruthy();
|
|
||||||
expect(ctrl.results[1].items[1].selected).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When navigating down two steps', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectedIndex = 0;
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select last dashboard in root folder', () => {
|
|
||||||
expect(ctrl.results[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].items[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].items[1].selected).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When navigating down three steps', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectedIndex = 0;
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select first folder', () => {
|
|
||||||
expect(ctrl.results[0].selected).toBeTruthy();
|
|
||||||
expect(ctrl.results[1].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].items[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].items[1].selected).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When navigating up one step', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectedIndex = 0;
|
|
||||||
ctrl.moveSelection(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select last dashboard in root folder', () => {
|
|
||||||
expect(ctrl.results[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].items[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].items[1].selected).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When navigating up two steps', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectedIndex = 0;
|
|
||||||
ctrl.moveSelection(-1);
|
|
||||||
ctrl.moveSelection(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select first dashboard in root folder', () => {
|
|
||||||
expect(ctrl.results[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].items[0].selected).toBeTruthy();
|
|
||||||
expect(ctrl.results[1].items[1].selected).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Given a result of one selected collapsed folder with 2 dashboards and a root folder with 2 dashboards', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.results = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
title: 'folder',
|
|
||||||
items: [
|
|
||||||
{ id: 2, selected: false },
|
|
||||||
{ id: 4, selected: false },
|
|
||||||
],
|
|
||||||
selected: true,
|
|
||||||
expanded: false,
|
|
||||||
toggle: (i: any) => (i.expanded = !i.expanded),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
title: 'General',
|
|
||||||
items: [
|
|
||||||
{ id: 3, selected: false },
|
|
||||||
{ id: 5, selected: false },
|
|
||||||
],
|
|
||||||
selected: false,
|
|
||||||
expanded: true,
|
|
||||||
toggle: (i: any) => (i.expanded = !i.expanded),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When navigating down one step', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectedIndex = 0;
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select first dashboard in root folder', () => {
|
|
||||||
expect(ctrl.results[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[0].items[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[0].items[1].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].items[0].selected).toBeTruthy();
|
|
||||||
expect(ctrl.results[1].items[1].selected).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When navigating down two steps', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectedIndex = 0;
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select last dashboard in root folder', () => {
|
|
||||||
expect(ctrl.results[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[0].items[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[0].items[1].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].items[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].items[1].selected).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When navigating down three steps', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectedIndex = 0;
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select first folder', () => {
|
|
||||||
expect(ctrl.results[0].selected).toBeTruthy();
|
|
||||||
expect(ctrl.results[1].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[0].items[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[0].items[1].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].items[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].items[1].selected).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When navigating up one step', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectedIndex = 0;
|
|
||||||
ctrl.moveSelection(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select last dashboard in root folder', () => {
|
|
||||||
expect(ctrl.results[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[0].items[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[0].items[1].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].items[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].items[1].selected).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When navigating up two steps', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectedIndex = 0;
|
|
||||||
ctrl.moveSelection(-1);
|
|
||||||
ctrl.moveSelection(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select first dashboard in root folder', () => {
|
|
||||||
expect(ctrl.results[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[1].items[0].selected).toBeTruthy();
|
|
||||||
expect(ctrl.results[1].items[1].selected).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Given a result of a search with 2 dashboards where the first is selected', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.results = [
|
|
||||||
{
|
|
||||||
hideHeader: true,
|
|
||||||
items: [
|
|
||||||
{ id: 3, selected: true },
|
|
||||||
{ id: 5, selected: false },
|
|
||||||
],
|
|
||||||
selected: false,
|
|
||||||
expanded: true,
|
|
||||||
toggle: (i: any) => (i.expanded = !i.expanded),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When navigating down one step', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectedIndex = 1;
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select last dashboard', () => {
|
|
||||||
expect(ctrl.results[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[0].items[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[0].items[1].selected).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When navigating down two steps', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectedIndex = 1;
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select first dashboard', () => {
|
|
||||||
expect(ctrl.results[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[0].items[0].selected).toBeTruthy();
|
|
||||||
expect(ctrl.results[0].items[1].selected).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When navigating down three steps', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectedIndex = 1;
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
ctrl.moveSelection(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select last dashboard', () => {
|
|
||||||
expect(ctrl.results[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[0].items[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[0].items[1].selected).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When navigating up one step', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectedIndex = 1;
|
|
||||||
ctrl.moveSelection(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select last dashboard', () => {
|
|
||||||
expect(ctrl.results[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[0].items[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[0].items[1].selected).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When navigating up two steps', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl.selectedIndex = 1;
|
|
||||||
ctrl.moveSelection(-1);
|
|
||||||
ctrl.moveSelection(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select first dashboard', () => {
|
|
||||||
expect(ctrl.results[0].selected).toBeFalsy();
|
|
||||||
expect(ctrl.results[0].items[0].selected).toBeTruthy();
|
|
||||||
expect(ctrl.results[0].items[1].selected).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,149 +0,0 @@
|
|||||||
import { ILocationService, IScope } from 'angular';
|
|
||||||
|
|
||||||
import { SearchResultsCtrl } from '../components/search/search_results';
|
|
||||||
import { afterEach, beforeEach } from 'test/lib/common';
|
|
||||||
import appEvents from 'app/core/app_events';
|
|
||||||
import { CoreEvents } from 'app/types';
|
|
||||||
|
|
||||||
jest.mock('app/core/app_events', () => {
|
|
||||||
return {
|
|
||||||
emit: jest.fn(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('SearchResultsCtrl', () => {
|
|
||||||
let ctrl: any;
|
|
||||||
const $location = {} as ILocationService;
|
|
||||||
const $scope = ({ $evalAsync: jest.fn() } as any) as IScope;
|
|
||||||
|
|
||||||
describe('when checking an item that is not checked', () => {
|
|
||||||
const item = { checked: false };
|
|
||||||
let selectionChanged = false;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl = new SearchResultsCtrl($location, $scope);
|
|
||||||
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', () => {
|
|
||||||
const item = { checked: true };
|
|
||||||
let selectionChanged = false;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl = new SearchResultsCtrl($location, $scope);
|
|
||||||
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: any = null;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl = new SearchResultsCtrl($location, $scope);
|
|
||||||
ctrl.onTagSelected = (tag: any) => (selectedTag = tag);
|
|
||||||
ctrl.selectTag('tag-test');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should trigger tag selected callback', () => {
|
|
||||||
expect(selectedTag['$tag']).toBe('tag-test');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when toggle a collapsed folder', () => {
|
|
||||||
let folderExpanded = false;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl = new SearchResultsCtrl($location, $scope);
|
|
||||||
ctrl.onFolderExpanding = () => {
|
|
||||||
folderExpanded = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const folder = {
|
|
||||||
expanded: false,
|
|
||||||
toggle: () => Promise.resolve(folder),
|
|
||||||
};
|
|
||||||
|
|
||||||
ctrl.toggleFolderExpand(folder);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should trigger folder expanding callback', () => {
|
|
||||||
expect(folderExpanded).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when toggle an expanded folder', () => {
|
|
||||||
let folderExpanded = false;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl = new SearchResultsCtrl($location, $scope);
|
|
||||||
ctrl.onFolderExpanding = () => {
|
|
||||||
folderExpanded = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const folder = {
|
|
||||||
expanded: true,
|
|
||||||
toggle: () => Promise.resolve(folder),
|
|
||||||
};
|
|
||||||
|
|
||||||
ctrl.toggleFolderExpand(folder);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not trigger folder expanding callback', () => {
|
|
||||||
expect(folderExpanded).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when clicking on a link in search result', () => {
|
|
||||||
const dashPath = 'dashboard/path';
|
|
||||||
const $location = ({ path: () => dashPath } as any) as ILocationService;
|
|
||||||
const appEventsMock = appEvents as any;
|
|
||||||
|
|
||||||
describe('with the same url as current path', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl = new SearchResultsCtrl($location, $scope);
|
|
||||||
const item = { url: dashPath };
|
|
||||||
ctrl.onItemClick(item);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should close the search', () => {
|
|
||||||
expect(appEventsMock.emit.mock.calls.length).toBe(1);
|
|
||||||
expect(appEventsMock.emit.mock.calls[0][0]).toBe(CoreEvents.hideDashSearch);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('with a different url than current path', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
ctrl = new SearchResultsCtrl($location, $scope);
|
|
||||||
const item = { url: 'another/path' };
|
|
||||||
ctrl.onItemClick(item);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should do nothing', () => {
|
|
||||||
expect(appEventsMock.emit.mock.calls.length).toBe(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
appEventsMock.emit.mockClear();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,10 +0,0 @@
|
|||||||
import { NavModelSrv } from 'app/core/core';
|
|
||||||
|
|
||||||
export class DashboardListCtrl {
|
|
||||||
navModel: any;
|
|
||||||
|
|
||||||
/** @ngInject */
|
|
||||||
constructor(navModelSrv: NavModelSrv) {
|
|
||||||
this.navModel = navModelSrv.getNav('dashboards', 'manage-dashboards', 0);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
import { IScope } from 'angular';
|
|
||||||
import { AppEvents } from '@grafana/data';
|
|
||||||
import coreModule from 'app/core/core_module';
|
|
||||||
import appEvents from 'app/core/app_events';
|
|
||||||
import { backendSrv } from 'app/core/services/backend_srv';
|
|
||||||
import { promiseToDigest } from 'app/core/utils/promiseToDigest';
|
|
||||||
|
|
||||||
export class MoveToFolderCtrl {
|
|
||||||
dashboards: any;
|
|
||||||
folder: any;
|
|
||||||
dismiss: any;
|
|
||||||
afterSave: any;
|
|
||||||
isValidFolderSelection = true;
|
|
||||||
|
|
||||||
constructor(private $scope: IScope) {}
|
|
||||||
|
|
||||||
onFolderChange = (folder: any) => {
|
|
||||||
this.folder = folder;
|
|
||||||
};
|
|
||||||
|
|
||||||
save = () => {
|
|
||||||
return promiseToDigest(this.$scope)(
|
|
||||||
backendSrv.moveDashboards(this.dashboards, this.folder).then((result: any) => {
|
|
||||||
if (result.successCount > 0) {
|
|
||||||
const header = `Dashboard${result.successCount === 1 ? '' : 's'} Moved`;
|
|
||||||
const msg = `${result.successCount} dashboard${result.successCount === 1 ? '' : 's'} moved to ${
|
|
||||||
this.folder.title
|
|
||||||
}`;
|
|
||||||
appEvents.emit(AppEvents.alertSuccess, [header, msg]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.totalCount === result.alreadyInFolderCount) {
|
|
||||||
appEvents.emit(AppEvents.alertError, ['Error', `Dashboards already belongs to folder ${this.folder.title}`]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dismiss();
|
|
||||||
return this.afterSave();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
onEnterFolderCreation = () => {
|
|
||||||
this.isValidFolderSelection = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
onExitFolderCreation = () => {
|
|
||||||
this.isValidFolderSelection = true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function moveToFolderModal() {
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
templateUrl: 'public/app/features/manage-dashboards/components/MoveToFolderModal/template.html',
|
|
||||||
controller: MoveToFolderCtrl,
|
|
||||||
bindToController: true,
|
|
||||||
controllerAs: 'ctrl',
|
|
||||||
scope: {
|
|
||||||
dismiss: '&',
|
|
||||||
dashboards: '=',
|
|
||||||
afterSave: '&',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
coreModule.directive('moveToFolderModal', moveToFolderModal);
|
|
@ -1 +0,0 @@
|
|||||||
export { MoveToFolderCtrl } from './MoveToFolderCtrl';
|
|
@ -1,38 +0,0 @@
|
|||||||
<div class="modal-body">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h2 class="modal-header-title">
|
|
||||||
<icon name="'folder-plus'" size="'lg'"></icon>
|
|
||||||
<span class="p-l-1">Choose Dashboard Folder</span>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<a class="modal-header-close" ng-click="ctrl.dismiss();">
|
|
||||||
<icon name="'times'"></icon>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form name="ctrl.saveForm" ng-submit="ctrl.save()" class="modal-content folder-modal" novalidate>
|
|
||||||
<p>Move the {{ctrl.dashboards.length}} selected dashboards to the following folder:</p>
|
|
||||||
|
|
||||||
<div class="p-t-2">
|
|
||||||
<div class="gf-form">
|
|
||||||
<folder-picker
|
|
||||||
on-change="ctrl.onFolderChange"
|
|
||||||
label-class="width-7"
|
|
||||||
enable-create-new="true"
|
|
||||||
folder="ctrl.folder"
|
|
||||||
>
|
|
||||||
</folder-picker>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form-button-row text-center">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
class="btn btn-primary"
|
|
||||||
ng-disabled="ctrl.saveForm.$invalid || !ctrl.isValidFolderSelection"
|
|
||||||
>
|
|
||||||
Move
|
|
||||||
</button>
|
|
||||||
<a class="btn-text" ng-click="ctrl.dismiss();">Cancel</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
@ -2,14 +2,11 @@
|
|||||||
export { ValidationSrv } from './services/ValidationSrv';
|
export { ValidationSrv } from './services/ValidationSrv';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
export * from './components/MoveToFolderModal';
|
|
||||||
export * from './components/UploadDashboard';
|
export * from './components/UploadDashboard';
|
||||||
|
|
||||||
// Controllers
|
// Controllers
|
||||||
import { DashboardListCtrl } from './DashboardListCtrl';
|
|
||||||
import { SnapshotListCtrl } from './SnapshotListCtrl';
|
import { SnapshotListCtrl } from './SnapshotListCtrl';
|
||||||
|
|
||||||
import coreModule from 'app/core/core_module';
|
import coreModule from 'app/core/core_module';
|
||||||
|
|
||||||
coreModule.controller('DashboardListCtrl', DashboardListCtrl);
|
|
||||||
coreModule.controller('SnapshotListCtrl', SnapshotListCtrl);
|
coreModule.controller('SnapshotListCtrl', SnapshotListCtrl);
|
||||||
|
@ -23,29 +23,24 @@ export const MoveToFolderModal: FC<Props> = ({ results, onMoveItems, isOpen, onD
|
|||||||
const selectedDashboards = getCheckedDashboards(results);
|
const selectedDashboards = getCheckedDashboards(results);
|
||||||
|
|
||||||
const moveTo = () => {
|
const moveTo = () => {
|
||||||
if (folder) {
|
if (folder && selectedDashboards.length) {
|
||||||
const folderTitle = folder.title ?? 'General';
|
const folderTitle = folder.title ?? 'General';
|
||||||
|
|
||||||
backendSrv
|
backendSrv.moveDashboards(selectedDashboards.map(d => d.uid) as string[], folder).then((result: any) => {
|
||||||
.moveDashboards(
|
if (result.successCount > 0) {
|
||||||
selectedDashboards.map(d => d.uid),
|
const ending = result.successCount === 1 ? '' : 's';
|
||||||
folder
|
const header = `Dashboard${ending} Moved`;
|
||||||
)
|
const msg = `${result.successCount} dashboard${ending} moved to ${folderTitle}`;
|
||||||
.then((result: any) => {
|
appEvents.emit(AppEvents.alertSuccess, [header, msg]);
|
||||||
if (result.successCount > 0) {
|
}
|
||||||
const ending = result.successCount === 1 ? '' : 's';
|
|
||||||
const header = `Dashboard${ending} Moved`;
|
|
||||||
const msg = `${result.successCount} dashboard${ending} moved to ${folderTitle}`;
|
|
||||||
appEvents.emit(AppEvents.alertSuccess, [header, msg]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.totalCount === result.alreadyInFolderCount) {
|
if (result.totalCount === result.alreadyInFolderCount) {
|
||||||
appEvents.emit(AppEvents.alertError, ['Error', `Dashboard already belongs to folder ${folderTitle}`]);
|
appEvents.emit(AppEvents.alertError, ['Error', `Dashboard already belongs to folder ${folderTitle}`]);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMoveItems(selectedDashboards, folder);
|
onMoveItems(selectedDashboards, folder);
|
||||||
onDismiss();
|
onDismiss();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { FC, MutableRefObject } from 'react';
|
import React, { FC } from 'react';
|
||||||
import { css, cx } from 'emotion';
|
import { css, cx } from 'emotion';
|
||||||
import { FixedSizeList } from 'react-window';
|
import { FixedSizeList } from 'react-window';
|
||||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||||
@ -18,7 +18,6 @@ export interface Props {
|
|||||||
onToggleSection: (section: DashboardSection) => void;
|
onToggleSection: (section: DashboardSection) => void;
|
||||||
results: DashboardSection[];
|
results: DashboardSection[];
|
||||||
layout?: string;
|
layout?: string;
|
||||||
wrapperRef?: MutableRefObject<HTMLDivElement | null>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SearchResults: FC<Props> = ({
|
export const SearchResults: FC<Props> = ({
|
||||||
@ -28,7 +27,6 @@ export const SearchResults: FC<Props> = ({
|
|||||||
onToggleChecked,
|
onToggleChecked,
|
||||||
onToggleSection,
|
onToggleSection,
|
||||||
results,
|
results,
|
||||||
wrapperRef,
|
|
||||||
layout,
|
layout,
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
@ -36,7 +36,7 @@ export const SectionHeader: FC<SectionHeaderProps> = ({
|
|||||||
[section]
|
[section]
|
||||||
);
|
);
|
||||||
|
|
||||||
return !section.hideHeader ? (
|
return (
|
||||||
<div className={styles.wrapper} onClick={onSectionExpand}>
|
<div className={styles.wrapper} onClick={onSectionExpand}>
|
||||||
<SearchCheckbox editable={editable} checked={section.checked} onClick={onSectionChecked} />
|
<SearchCheckbox editable={editable} checked={section.checked} onClick={onSectionChecked} />
|
||||||
<Icon className={styles.icon} name={section.icon as IconName} />
|
<Icon className={styles.icon} name={section.icon as IconName} />
|
||||||
@ -49,8 +49,6 @@ export const SectionHeader: FC<SectionHeaderProps> = ({
|
|||||||
)}
|
)}
|
||||||
<Icon name={section.expanded ? 'angle-down' : 'angle-right'} />
|
<Icon name={section.expanded ? 'angle-down' : 'angle-right'} />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<div className={styles.wrapper} />
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,10 +24,6 @@ export const useSearch: UseSearch = (query, reducer, params) => {
|
|||||||
dispatch({ type: SEARCH_START });
|
dispatch({ type: SEARCH_START });
|
||||||
const parsedQuery = getParsedQuery(query, queryParsing);
|
const parsedQuery = getParsedQuery(query, queryParsing);
|
||||||
searchSrv.search(parsedQuery).then(results => {
|
searchSrv.search(parsedQuery).then(results => {
|
||||||
// Remove header for folder search
|
|
||||||
if (query.folderIds.length === 1 && results.length) {
|
|
||||||
results[0].hideHeader = true;
|
|
||||||
}
|
|
||||||
dispatch({ type: FETCH_RESULTS, payload: results });
|
dispatch({ type: FETCH_RESULTS, payload: results });
|
||||||
|
|
||||||
if (searchCallback) {
|
if (searchCallback) {
|
||||||
|
@ -7,4 +7,3 @@ export { SearchResultsFilter } from './components/SearchResultsFilter';
|
|||||||
export { ManageDashboards } from './components/ManageDashboards';
|
export { ManageDashboards } from './components/ManageDashboards';
|
||||||
export { ConfirmDeleteModal } from './components/ConfirmDeleteModal';
|
export { ConfirmDeleteModal } from './components/ConfirmDeleteModal';
|
||||||
export { MoveToFolderModal } from './components/MoveToFolderModal';
|
export { MoveToFolderModal } from './components/MoveToFolderModal';
|
||||||
export * from './types';
|
|
||||||
|
@ -15,14 +15,14 @@ export interface DashboardSection {
|
|||||||
title: string;
|
title: string;
|
||||||
expanded?: boolean;
|
expanded?: boolean;
|
||||||
url: string;
|
url: string;
|
||||||
icon: string;
|
icon?: string;
|
||||||
score: number;
|
score?: number;
|
||||||
hideHeader?: boolean;
|
|
||||||
checked?: boolean;
|
checked?: boolean;
|
||||||
items: DashboardSectionItem[];
|
items: DashboardSectionItem[];
|
||||||
toggle?: (section: DashboardSection) => Promise<DashboardSection>;
|
toggle?: (section: DashboardSection) => Promise<DashboardSection>;
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
type: DashboardSearchItemType;
|
type: DashboardSearchItemType;
|
||||||
|
slug?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DashboardSectionItem {
|
export interface DashboardSectionItem {
|
||||||
@ -37,11 +37,13 @@ export interface DashboardSectionItem {
|
|||||||
tags: string[];
|
tags: string[];
|
||||||
title: string;
|
title: string;
|
||||||
type: DashboardSearchItemType;
|
type: DashboardSearchItemType;
|
||||||
uid: string;
|
uid?: string;
|
||||||
uri: string;
|
uri: string;
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DashboardSearchHit extends DashboardSectionItem, DashboardSection {}
|
||||||
|
|
||||||
export interface DashboardTag {
|
export interface DashboardTag {
|
||||||
term: string;
|
term: string;
|
||||||
count: number;
|
count: number;
|
||||||
|
@ -10,7 +10,6 @@ export * from './datasources';
|
|||||||
export * from './plugins';
|
export * from './plugins';
|
||||||
export * from './organization';
|
export * from './organization';
|
||||||
export * from './appNotifications';
|
export * from './appNotifications';
|
||||||
export * from './search';
|
|
||||||
export * from './explore';
|
export * from './explore';
|
||||||
export * from './store';
|
export * from './store';
|
||||||
export * from './ldap';
|
export * from './ldap';
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
export enum DashboardSearchHitType {
|
|
||||||
DashHitDB = 'dash-db',
|
|
||||||
DashHitHome = 'dash-home',
|
|
||||||
DashHitFolder = 'dash-folder',
|
|
||||||
}
|
|
||||||
export interface DashboardSearchHit {
|
|
||||||
folderId?: number;
|
|
||||||
folderTitle?: string;
|
|
||||||
folderUid?: string;
|
|
||||||
folderUrl?: string;
|
|
||||||
id: number;
|
|
||||||
isStarred: boolean;
|
|
||||||
slug: string;
|
|
||||||
tags: string[];
|
|
||||||
title: string;
|
|
||||||
type: DashboardSearchHitType;
|
|
||||||
uid: string;
|
|
||||||
uri: string;
|
|
||||||
url: string;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user