SearchV2: add CTA and reporting to new search UI (#50175)

This commit is contained in:
Ryan McKinley 2022-06-03 08:22:17 -07:00 committed by GitHub
parent 7a614fd8a1
commit 1a324b3330
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 99 additions and 9 deletions

View File

@ -95,7 +95,7 @@ export const ActionRow: FC<Props> = ({
Datasource: {query.datasource}
</Button>
)}
{layout !== SearchLayout.Folders && (
{layout !== SearchLayout.Folders && config.featureToggles.panelTitleSearch && (
<Checkbox value={includePanels} onChange={() => setIncludePanels(!includePanels)} label="Include panels" />
)}

View File

@ -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 <div className={styles.unsupported}>Unsupported</div>;
}
// This gets the possible tags from within the query results
const getTagOptions = (): Promise<TermCount[]> => {
return getGrafanaSearcher().tags(searchQuery);
@ -197,8 +209,19 @@ export const SearchView = ({
);
};
if (!config.featureToggles.panelTitleSearch) {
return <div className={styles.unsupported}>Unsupported</div>;
if (folderDTO && !results.loading && !results.value?.totalRows && !queryText.length) {
return (
<EmptyListCTA
title="This folder doesn't have any dashboards yet"
buttonIcon="plus"
buttonTitle="Create Dashboard"
buttonLink={`dashboard/new?folderId=${folderDTO.id}`}
proTip="Add/move dashboards to your folder at ->"
proTipLink="dashboards"
proTipLinkTitle="Manage dashboards"
proTipTarget=""
/>
);
}
return (

View File

@ -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,
});
};

View File

@ -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<QueryResponse> {
query = await replaceCurrentFolderQuery(query);
const ds = (await getDataSourceSrv().get('-- Grafana --')) as GrafanaDatasource;
const target = {
...query,

View File

@ -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';

View File

@ -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<SearchQuery> {
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<string | undefined> {
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));
}