Skip to content

Commit

Permalink
increase default number of results to show all apps
Browse files Browse the repository at this point in the history
  • Loading branch information
pgayvallet committed Sep 29, 2020
1 parent faf4b04 commit 3aca649
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 35 deletions.
7 changes: 7 additions & 0 deletions x-pack/plugins/global_search/common/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export const defaultMaxProviderResults = 40;
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { HttpStart } from 'src/core/public';
import { GlobalSearchProviderResult, GlobalSearchBatchedResults } from '../../common/types';
import { GlobalSearchFindError } from '../../common/errors';
import { takeInArray } from '../../common/operators';
import { defaultMaxProviderResults } from '../../common/constants';
import { processProviderResult } from '../../common/process_result';
import { ILicenseChecker } from '../../common/license_checker';
import { GlobalSearchResultProvider } from '../types';
Expand Down Expand Up @@ -79,7 +80,6 @@ interface StartDeps {
licenseChecker: ILicenseChecker;
}

const defaultMaxProviderResults = 20;
const mapToUndefined = () => undefined;

/** @internal */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { KibanaRequest, CoreStart, IBasePath } from 'src/core/server';
import { GlobalSearchProviderResult, GlobalSearchBatchedResults } from '../../common/types';
import { GlobalSearchFindError } from '../../common/errors';
import { takeInArray } from '../../common/operators';
import { defaultMaxProviderResults } from '../../common/constants';
import { ILicenseChecker } from '../../common/license_checker';

import { processProviderResult } from '../../common/process_result';
Expand Down Expand Up @@ -80,7 +81,6 @@ interface StartDeps {
licenseChecker: ILicenseChecker;
}

const defaultMaxProviderResults = 20;
const mapToUndefined = () => undefined;

/** @internal */
Expand Down
82 changes: 51 additions & 31 deletions x-pack/plugins/global_search_bar/public/components/search_bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { Subscription } from 'rxjs';
import { ApplicationStart } from 'kibana/public';
import React, { useCallback, useState } from 'react';
import React, { useCallback, useState, useRef } from 'react';
import useDebounce from 'react-use/lib/useDebounce';
import useEvent from 'react-use/lib/useEvent';
import useMountedState from 'react-use/lib/useMountedState';
Expand All @@ -42,62 +43,81 @@ const clearField = (field: HTMLInputElement) => {
const cleanMeta = (str: string) => (str.charAt(0).toUpperCase() + str.slice(1)).replace(/-/g, ' ');
const blurEvent = new FocusEvent('blur');

const sortByScore = (a: GlobalSearchResult, b: GlobalSearchResult): number => {
if (a.score < b.score) return 1;
if (a.score > b.score) return -1;
return 0;
};

const sortByTitle = (a: GlobalSearchResult, b: GlobalSearchResult): number => {
const titleA = a.title.toUpperCase(); // ignore upper and lowercase
const titleB = b.title.toUpperCase(); // ignore upper and lowercase
if (titleA < titleB) return -1;
if (titleA > titleB) return 1;
return 0;
};

const resultToOption = (result: GlobalSearchResult): EuiSelectableTemplateSitewideOption => {
const { id, title, url, icon, type, meta } = result;
const option: EuiSelectableTemplateSitewideOption = {
key: id,
label: title,
url,
};

if (icon) {
option.icon = { type: icon };
}

if (type === 'application') {
option.meta = [{ text: meta?.categoryLabel as string }];
} else {
option.meta = [{ text: cleanMeta(type) }];
}

return option;
};

export function SearchBar({ globalSearch, navigateToUrl }: Props) {
const isMounted = useMountedState();
const [searchValue, setSearchValue] = useState<string>('');
const [searchRef, setSearchRef] = useState<HTMLInputElement | null>(null);
const searchSubscription = useRef<Subscription | null>(null);
const [options, _setOptions] = useState([] as EuiSelectableTemplateSitewideOption[]);
const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0;

const setOptions = useCallback(
(_options: GlobalSearchResult[]) => {
if (!isMounted()) return;

_setOptions([
..._options.map(({ id, title, url, icon, type, meta }) => {
const option: EuiSelectableTemplateSitewideOption = {
key: id,
label: title,
url,
};

if (icon) option.icon = { type: icon };

if (type === 'application') option.meta = [{ text: meta?.categoryLabel as string }];
else option.meta = [{ text: cleanMeta(type) }];
if (!isMounted()) {
return;
}

return option;
}),
]);
_setOptions(_options.map(resultToOption));
},
[isMounted, _setOptions]
);

useDebounce(
() => {
// cancel pending search if not completed yet
if (searchSubscription.current) {
searchSubscription.current.unsubscribe();
searchSubscription.current = null;
}

let arr: GlobalSearchResult[] = [];
globalSearch(searchValue, {}).subscribe({
searchSubscription.current = globalSearch(searchValue, {}).subscribe({
next: ({ results }) => {
if (searchValue.length > 0) {
arr = [...results, ...arr].sort((a, b) => {
if (a.score < b.score) return 1;
if (a.score > b.score) return -1;
return 0;
});
arr = [...results, ...arr].sort(sortByScore);
setOptions(arr);
return;
}

// if searchbar is empty, filter to only applications and sort alphabetically
results = results.filter(({ type }: GlobalSearchResult) => type === 'application');

arr = [...results, ...arr].sort((a, b) => {
const titleA = a.title.toUpperCase(); // ignore upper and lowercase
const titleB = b.title.toUpperCase(); // ignore upper and lowercase
if (titleA < titleB) return -1;
if (titleA > titleB) return 1;
return 0;
});
arr = [...results, ...arr].sort(sortByTitle);

setOptions(arr);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import { EMPTY } from 'rxjs';
import { toArray } from 'rxjs/operators';
import { TestScheduler } from 'rxjs/testing';
import {
SavedObjectsFindResponse,
Expand Down Expand Up @@ -114,8 +115,8 @@ describe('savedObjectsResultProvider', () => {
expect(provider.id).toBe('savedObjects');
});

it('calls `savedObjectClient.find` with the correct parameters', () => {
provider.find('term', defaultOption, context);
it('calls `savedObjectClient.find` with the correct parameters', async () => {
await provider.find('term', defaultOption, context).toPromise();

expect(context.core.savedObjects.client.find).toHaveBeenCalledTimes(1);
expect(context.core.savedObjects.client.find).toHaveBeenCalledWith({
Expand All @@ -128,6 +129,13 @@ describe('savedObjectsResultProvider', () => {
});
});

it('does not call `savedObjectClient.find` if `term` is empty', async () => {
const results = await provider.find('', defaultOption, context).pipe(toArray()).toPromise();

expect(context.core.savedObjects.client.find).not.toHaveBeenCalled();
expect(results).toEqual([]);
});

it('converts the saved objects to results', async () => {
context.core.savedObjects.client.find.mockResolvedValue(
createFindResponse([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export const createSavedObjectsResultProvider = (): GlobalSearchResultProvider =
return {
id: 'savedObjects',
find: (term, { aborted$, maxResults, preference }, { core }) => {
if (!term) {
return from([]);
}

const {
capabilities,
savedObjects: { client, typeRegistry },
Expand Down

0 comments on commit 3aca649

Please sign in to comment.