diff --git a/public/app/features/search/page/components/ActionRow.tsx b/public/app/features/search/page/components/ActionRow.tsx index 6d974bc1747..da1e7d1c21a 100644 --- a/public/app/features/search/page/components/ActionRow.tsx +++ b/public/app/features/search/page/components/ActionRow.tsx @@ -95,7 +95,7 @@ export const ActionRow: FC = ({ Datasource: {query.datasource} )} - {layout !== SearchLayout.Folders && ( + {layout !== SearchLayout.Folders && config.featureToggles.panelTitleSearch && ( setIncludePanels(!includePanels)} label="Include panels" /> )} diff --git a/public/app/features/search/page/components/SearchView.tsx b/public/app/features/search/page/components/SearchView.tsx index 78344228c11..f224a75f83a 100644 --- a/public/app/features/search/page/components/SearchView.tsx +++ b/public/app/features/search/page/components/SearchView.tsx @@ -1,11 +1,11 @@ import { css } from '@emotion/css'; import React, { useCallback, useMemo, useState } from 'react'; -import { useAsync } from 'react-use'; +import { useAsync, useDebounce } from 'react-use'; import AutoSizer from 'react-virtualized-auto-sizer'; import { GrafanaTheme2 } from '@grafana/data'; -import { config } from '@grafana/runtime'; import { useStyles2, Spinner, Button } from '@grafana/ui'; +import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import { TermCount } from 'app/core/components/TagFilter/TagFilter'; import { FolderDTO } from 'app/types'; @@ -13,6 +13,7 @@ import { PreviewsSystemRequirements } from '../../components/PreviewsSystemRequi import { useSearchQuery } from '../../hooks/useSearchQuery'; import { getGrafanaSearcher, SearchQuery } from '../../service'; import { SearchLayout } from '../../types'; +import { reportDashboardListViewed } from '../reporting'; import { newSearchSelection, updateSearchSelection } from '../selection'; import { ActionRow, getValidQueryLayout } from './ActionRow'; @@ -80,6 +81,21 @@ export const SearchView = ({ return q; }, [query, queryText, folderDTO, includePanels]); + // Search usage reporting + useDebounce( + () => { + reportDashboardListViewed(folderDTO ? 'manage_dashboards' : 'dashboard_search', { + layout: query.layout, + starred: query.starred, + sortValue: query.sort?.value, + query: query.query, + tagCount: query.tag?.length, + }); + }, + 1000, + [folderDTO, query.layout, query.starred, query.sort?.value, query.query?.length, query.tag?.length] + ); + const results = useAsync(() => { return getGrafanaSearcher().search(searchQuery); }, [searchQuery]); @@ -97,10 +113,6 @@ export const SearchView = ({ [searchSelection] ); - if (!config.featureToggles.panelTitleSearch) { - return
Unsupported
; - } - // This gets the possible tags from within the query results const getTagOptions = (): Promise => { return getGrafanaSearcher().tags(searchQuery); @@ -197,8 +209,19 @@ export const SearchView = ({ ); }; - if (!config.featureToggles.panelTitleSearch) { - return
Unsupported
; + if (folderDTO && !results.loading && !results.value?.totalRows && !queryText.length) { + return ( + + ); } return ( diff --git a/public/app/features/search/page/reporting.ts b/public/app/features/search/page/reporting.ts new file mode 100644 index 00000000000..34e9d51c12c --- /dev/null +++ b/public/app/features/search/page/reporting.ts @@ -0,0 +1,26 @@ +import { config, reportInteraction } from '@grafana/runtime'; + +import { SearchLayout } from '../types'; + +export const reportDashboardListViewed = ( + dashboardListType: 'manage_dashboards' | 'dashboard_search', + query: { + layout?: SearchLayout; + starred?: boolean; + sortValue?: string; + query?: string; + tagCount?: number; + } +) => { + const showPreviews = query.layout === SearchLayout.Grid; + const previewsEnabled = Boolean(config.featureToggles.panelTitleSearch); + const previews = previewsEnabled ? (showPreviews ? 'on' : 'off') : 'feature_disabled'; + reportInteraction(`${dashboardListType}_viewed`, { + previews, + layout: query.layout, + starredFilter: query.starred ?? false, + sort: query.sortValue ?? '', + tagCount: query.tagCount ?? 0, + queryLength: query.query?.length ?? 0, + }); +}; diff --git a/public/app/features/search/service/bluge.ts b/public/app/features/search/service/bluge.ts index 38886ba369b..fd1c9a835eb 100644 --- a/public/app/features/search/service/bluge.ts +++ b/public/app/features/search/service/bluge.ts @@ -6,6 +6,8 @@ import { TermCount } from 'app/core/components/TagFilter/TagFilter'; import { GrafanaDatasource } from 'app/plugins/datasource/grafana/datasource'; import { GrafanaQueryType } from 'app/plugins/datasource/grafana/types'; +import { replaceCurrentFolderQuery } from './utils'; + import { DashboardQueryResult, GrafanaSearcher, QueryResponse, SearchQuery, SearchResultMeta } from '.'; export class BlugeSearcher implements GrafanaSearcher { @@ -65,6 +67,7 @@ const firstPageSize = 50; const nextPageSizes = 100; async function doSearchQuery(query: SearchQuery): Promise { + query = await replaceCurrentFolderQuery(query); const ds = (await getDataSourceSrv().get('-- Grafana --')) as GrafanaDatasource; const target = { ...query, diff --git a/public/app/features/search/service/sql.ts b/public/app/features/search/service/sql.ts index d20b38b7001..e1b91406973 100644 --- a/public/app/features/search/service/sql.ts +++ b/public/app/features/search/service/sql.ts @@ -6,6 +6,7 @@ import { backendSrv } from 'app/core/services/backend_srv'; import { DashboardSearchHit } from '../types'; import { LocationInfo } from './types'; +import { replaceCurrentFolderQuery } from './utils'; import { DashboardQueryResult, GrafanaSearcher, QueryResponse, SearchQuery } from '.'; @@ -46,6 +47,7 @@ export class SQLSearcher implements GrafanaSearcher { sort: query.sort, }; + query = await replaceCurrentFolderQuery(query); if (query.query === '*') { if (query.kind?.length === 1 && query.kind[0] === 'folder') { q.type = 'dash-folder'; diff --git a/public/app/features/search/service/utils.ts b/public/app/features/search/service/utils.ts new file mode 100644 index 00000000000..2d080d4f2a9 --- /dev/null +++ b/public/app/features/search/service/utils.ts @@ -0,0 +1,36 @@ +import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; + +import { SearchQuery } from './types'; + +/** prepare the query replacing folder:current */ +export async function replaceCurrentFolderQuery(query: SearchQuery): Promise { + if (query.query && query.query.indexOf('folder:current') >= 0) { + query = { + ...query, + location: await getCurrentFolderUID(), + query: query.query.replace('folder:current', '').trim(), + }; + if (!query.query?.length) { + query.query = '*'; + } + } + return Promise.resolve(query); +} + +async function getCurrentFolderUID(): Promise { + try { + let dash = getDashboardSrv().getCurrent(); + if (!dash) { + await delay(500); // may not be loaded yet + dash = getDashboardSrv().getCurrent(); + } + return Promise.resolve(dash?.meta?.folderUid); + } catch (e) { + console.error(e); + } + return undefined; +} + +function delay(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +}