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 { AsyncSelect } from '@grafana/ui';
|
||||
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 {
|
||||
onSelected: (dashboard: DashboardDTO) => void;
|
||||
|
@ -5,7 +5,7 @@ import { debounce } from 'lodash';
|
||||
import appEvents from '../../app_events';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import { DashboardSearchHit } from '../../../types';
|
||||
import { DashboardSearchHit } from 'app/features/search/types';
|
||||
|
||||
export interface Props {
|
||||
onChange: ($folder: { title: string; id: number }) => void;
|
||||
|
@ -3,7 +3,7 @@ import React, { PureComponent } from 'react';
|
||||
import { InlineFormLabel, LegacyForms } from '@grafana/ui';
|
||||
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';
|
||||
|
||||
export interface Props {
|
||||
@ -50,7 +50,7 @@ export class SharedPreferences extends PureComponent<Props, State> {
|
||||
id: 0,
|
||||
title: 'Default',
|
||||
tags: [],
|
||||
type: '' as DashboardSearchHitType,
|
||||
type: '' as DashboardSearchItemType,
|
||||
uid: '',
|
||||
uri: '',
|
||||
url: '',
|
||||
@ -60,6 +60,7 @@ export class SharedPreferences extends PureComponent<Props, State> {
|
||||
folderUrl: '',
|
||||
isStarred: false,
|
||||
slug: '',
|
||||
items: [],
|
||||
};
|
||||
|
||||
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 { colors, JsonExplorer } from '@grafana/ui/';
|
||||
|
||||
import { searchDirective } from './components/search/search';
|
||||
import { infoPopover } from './components/info_popover';
|
||||
import { arrayJoin } from './directives/array_join';
|
||||
import { liveSrv } from './live/live_srv';
|
||||
@ -42,8 +41,6 @@ import { profiler } from './profiler';
|
||||
import { registerAngularDirectives } from './angular_wrappers';
|
||||
import { updateLegendValues } 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';
|
||||
|
||||
export {
|
||||
@ -51,7 +48,6 @@ export {
|
||||
registerAngularDirectives,
|
||||
arrayJoin,
|
||||
coreModule,
|
||||
searchDirective,
|
||||
liveSrv,
|
||||
switchDirective,
|
||||
infoPopover,
|
||||
@ -69,8 +65,6 @@ export {
|
||||
NavModelSrv,
|
||||
NavModel,
|
||||
geminiScrollbar,
|
||||
manageDashboardsDirective,
|
||||
TimeSeries,
|
||||
updateLegendValues,
|
||||
searchResultsDirective,
|
||||
};
|
||||
|
@ -7,12 +7,12 @@ import { AppEvents } from '@grafana/data';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import config from 'app/core/config';
|
||||
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 { ContextSrv, contextSrv } from './context_srv';
|
||||
import { coreModule } from 'app/core/core_module';
|
||||
import { ContextSrv, contextSrv } from './context_srv';
|
||||
import { Emitter } from '../utils/emitter';
|
||||
import { DataSourceResponse } from '../../types/events';
|
||||
import { parseInitFromOptions, parseUrlFromOptions } from '../utils/fetch';
|
||||
|
||||
export interface DatasourceRequestOptions {
|
||||
|
@ -1,16 +1,14 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
import coreModule from 'app/core/core_module';
|
||||
import impressionSrv from 'app/core/services/impression_srv';
|
||||
import store from 'app/core/store';
|
||||
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 { Section } from '../components/manage_dashboards/manage_dashboards';
|
||||
import { DashboardSearchHit, DashboardSearchHitType } from 'app/types/search';
|
||||
import { hasFilters } from '../../features/search/utils';
|
||||
|
||||
interface Sections {
|
||||
[key: string]: Partial<Section>;
|
||||
[key: string]: Partial<DashboardSection>;
|
||||
}
|
||||
|
||||
export class SearchSrv {
|
||||
@ -22,18 +20,16 @@ export class SearchSrv {
|
||||
this.starredIsOpen = store.getBool('search.sections.starred', true);
|
||||
}
|
||||
|
||||
private getRecentDashboards(sections: Sections) {
|
||||
private getRecentDashboards(sections: DashboardSection[] | any) {
|
||||
return this.queryForRecentDashboards().then((result: any[]) => {
|
||||
if (result.length > 0) {
|
||||
sections['recent'] = {
|
||||
title: 'Recent',
|
||||
icon: 'clock-nine',
|
||||
score: -1,
|
||||
removable: true,
|
||||
expanded: this.recentIsOpen,
|
||||
toggle: this.toggleRecent.bind(this),
|
||||
items: result,
|
||||
type: DashboardSearchHitType.DashHitFolder,
|
||||
type: DashboardSearchItemType.DashFolder,
|
||||
};
|
||||
}
|
||||
});
|
||||
@ -50,45 +46,24 @@ export class SearchSrv {
|
||||
.map(orderId => {
|
||||
return _.find(result, { id: orderId });
|
||||
})
|
||||
.filter(hit => hit && !hit.isStarred);
|
||||
.filter(hit => hit && !hit.isStarred) as DashboardSearchHit[];
|
||||
});
|
||||
}
|
||||
|
||||
private toggleRecent(section: Section) {
|
||||
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) {
|
||||
private getStarred(sections: DashboardSection) {
|
||||
if (!contextSrv.isSignedIn) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return backendSrv.search({ starred: true, limit: 30 }).then(result => {
|
||||
if (result.length > 0) {
|
||||
sections['starred'] = {
|
||||
(sections as any)['starred'] = {
|
||||
title: 'Starred',
|
||||
icon: 'star',
|
||||
score: -2,
|
||||
expanded: this.starredIsOpen,
|
||||
toggle: this.toggleStarred.bind(this),
|
||||
items: result,
|
||||
type: DashboardSearchHitType.DashHitFolder,
|
||||
type: DashboardSearchItemType.DashFolder,
|
||||
};
|
||||
}
|
||||
});
|
||||
@ -138,7 +113,6 @@ export class SearchSrv {
|
||||
title: hit.title,
|
||||
expanded: false,
|
||||
items: [],
|
||||
toggle: this.toggleFolder.bind(this),
|
||||
url: hit.url,
|
||||
icon: 'folder',
|
||||
score: _.keys(sections).length,
|
||||
@ -162,9 +136,8 @@ export class SearchSrv {
|
||||
url: hit.folderUrl,
|
||||
items: [],
|
||||
icon: 'folder-open',
|
||||
toggle: this.toggleFolder.bind(this),
|
||||
score: _.keys(sections).length,
|
||||
type: DashboardSearchHitType.DashHitFolder,
|
||||
type: DashboardSearchItemType.DashFolder,
|
||||
};
|
||||
} else {
|
||||
section = {
|
||||
@ -172,9 +145,8 @@ export class SearchSrv {
|
||||
title: 'General',
|
||||
items: [],
|
||||
icon: 'folder-open',
|
||||
toggle: this.toggleFolder.bind(this),
|
||||
score: _.keys(sections).length,
|
||||
type: DashboardSearchHitType.DashHitFolder,
|
||||
type: DashboardSearchItemType.DashFolder,
|
||||
};
|
||||
}
|
||||
// add section
|
||||
@ -182,28 +154,10 @@ export class SearchSrv {
|
||||
}
|
||||
|
||||
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() {
|
||||
return backendSrv.get('/api/dashboards/tags');
|
||||
}
|
||||
@ -212,5 +166,3 @@ export class SearchSrv {
|
||||
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';
|
||||
|
||||
// Components
|
||||
export * from './components/MoveToFolderModal';
|
||||
export * from './components/UploadDashboard';
|
||||
|
||||
// Controllers
|
||||
import { DashboardListCtrl } from './DashboardListCtrl';
|
||||
import { SnapshotListCtrl } from './SnapshotListCtrl';
|
||||
|
||||
import coreModule from 'app/core/core_module';
|
||||
|
||||
coreModule.controller('DashboardListCtrl', DashboardListCtrl);
|
||||
coreModule.controller('SnapshotListCtrl', SnapshotListCtrl);
|
||||
|
@ -23,29 +23,24 @@ export const MoveToFolderModal: FC<Props> = ({ results, onMoveItems, isOpen, onD
|
||||
const selectedDashboards = getCheckedDashboards(results);
|
||||
|
||||
const moveTo = () => {
|
||||
if (folder) {
|
||||
if (folder && selectedDashboards.length) {
|
||||
const folderTitle = folder.title ?? 'General';
|
||||
|
||||
backendSrv
|
||||
.moveDashboards(
|
||||
selectedDashboards.map(d => d.uid),
|
||||
folder
|
||||
)
|
||||
.then((result: any) => {
|
||||
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]);
|
||||
}
|
||||
backendSrv.moveDashboards(selectedDashboards.map(d => d.uid) as string[], folder).then((result: any) => {
|
||||
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) {
|
||||
appEvents.emit(AppEvents.alertError, ['Error', `Dashboard already belongs to folder ${folderTitle}`]);
|
||||
}
|
||||
if (result.totalCount === result.alreadyInFolderCount) {
|
||||
appEvents.emit(AppEvents.alertError, ['Error', `Dashboard already belongs to folder ${folderTitle}`]);
|
||||
}
|
||||
|
||||
onMoveItems(selectedDashboards, folder);
|
||||
onDismiss();
|
||||
});
|
||||
onMoveItems(selectedDashboards, folder);
|
||||
onDismiss();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { FC, MutableRefObject } from 'react';
|
||||
import React, { FC } from 'react';
|
||||
import { css, cx } from 'emotion';
|
||||
import { FixedSizeList } from 'react-window';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
@ -18,7 +18,6 @@ export interface Props {
|
||||
onToggleSection: (section: DashboardSection) => void;
|
||||
results: DashboardSection[];
|
||||
layout?: string;
|
||||
wrapperRef?: MutableRefObject<HTMLDivElement | null>;
|
||||
}
|
||||
|
||||
export const SearchResults: FC<Props> = ({
|
||||
@ -28,7 +27,6 @@ export const SearchResults: FC<Props> = ({
|
||||
onToggleChecked,
|
||||
onToggleSection,
|
||||
results,
|
||||
wrapperRef,
|
||||
layout,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
@ -36,7 +36,7 @@ export const SectionHeader: FC<SectionHeaderProps> = ({
|
||||
[section]
|
||||
);
|
||||
|
||||
return !section.hideHeader ? (
|
||||
return (
|
||||
<div className={styles.wrapper} onClick={onSectionExpand}>
|
||||
<SearchCheckbox editable={editable} checked={section.checked} onClick={onSectionChecked} />
|
||||
<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'} />
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.wrapper} />
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -24,10 +24,6 @@ export const useSearch: UseSearch = (query, reducer, params) => {
|
||||
dispatch({ type: SEARCH_START });
|
||||
const parsedQuery = getParsedQuery(query, queryParsing);
|
||||
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 });
|
||||
|
||||
if (searchCallback) {
|
||||
|
@ -7,4 +7,3 @@ export { SearchResultsFilter } from './components/SearchResultsFilter';
|
||||
export { ManageDashboards } from './components/ManageDashboards';
|
||||
export { ConfirmDeleteModal } from './components/ConfirmDeleteModal';
|
||||
export { MoveToFolderModal } from './components/MoveToFolderModal';
|
||||
export * from './types';
|
||||
|
@ -15,14 +15,14 @@ export interface DashboardSection {
|
||||
title: string;
|
||||
expanded?: boolean;
|
||||
url: string;
|
||||
icon: string;
|
||||
score: number;
|
||||
hideHeader?: boolean;
|
||||
icon?: string;
|
||||
score?: number;
|
||||
checked?: boolean;
|
||||
items: DashboardSectionItem[];
|
||||
toggle?: (section: DashboardSection) => Promise<DashboardSection>;
|
||||
selected?: boolean;
|
||||
type: DashboardSearchItemType;
|
||||
slug?: string;
|
||||
}
|
||||
|
||||
export interface DashboardSectionItem {
|
||||
@ -37,11 +37,13 @@ export interface DashboardSectionItem {
|
||||
tags: string[];
|
||||
title: string;
|
||||
type: DashboardSearchItemType;
|
||||
uid: string;
|
||||
uid?: string;
|
||||
uri: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface DashboardSearchHit extends DashboardSectionItem, DashboardSection {}
|
||||
|
||||
export interface DashboardTag {
|
||||
term: string;
|
||||
count: number;
|
||||
|
@ -10,7 +10,6 @@ export * from './datasources';
|
||||
export * from './plugins';
|
||||
export * from './organization';
|
||||
export * from './appNotifications';
|
||||
export * from './search';
|
||||
export * from './explore';
|
||||
export * from './store';
|
||||
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