[Fleet] Add icons to Integrations global search results (#111131)

This commit is contained in:
Josh Dover 2021-09-07 14:27:10 +02:00 committed by GitHub
parent 51fd4abe20
commit 099a63ea2a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 67 additions and 12 deletions

View file

@ -10,7 +10,7 @@ import { NEVER } from 'rxjs';
import { coreMock } from 'src/core/public/mocks'; import { coreMock } from 'src/core/public/mocks';
import { createPackageSearchProvider } from './search_provider'; import { createPackageSearchProvider, toSearchResult } from './search_provider';
import type { GetPackagesResponse } from './types'; import type { GetPackagesResponse } from './types';
jest.mock('./hooks/use_request/epm', () => { jest.mock('./hooks/use_request/epm', () => {
@ -286,4 +286,42 @@ describe('Package search provider', () => {
}); });
}); });
}); });
describe('toSearchResult', () => {
let startMock: ReturnType<typeof coreMock.createStart>;
beforeEach(() => {
startMock = coreMock.createStart();
});
it('uses svg icon if available', () => {
const pkg = {
...testResponse[0],
icons: [{ type: 'image/svg+xml', src: '/img_nginx.svg', path: '' }],
};
const { icon } = toSearchResult(pkg, startMock.application, startMock.http.basePath);
expect(icon).toMatchInlineSnapshot(`"/api/fleet/epm/packages/test/test/img_nginx.svg"`);
});
it('prepends base path to svg URL', () => {
startMock = coreMock.createStart({ basePath: '/foo' });
const pkg = {
...testResponse[0],
icons: [{ type: 'image/svg+xml', src: '/img_nginx.svg', path: '' }],
};
const { icon } = toSearchResult(pkg, startMock.application, startMock.http.basePath);
expect(icon).toMatchInlineSnapshot(`"/foo/api/fleet/epm/packages/test/test/img_nginx.svg"`);
});
// ICON_TYPES is empty in EUI: https://github.com/elastic/eui/issues/5138
it.skip('uses eui icon type as fallback', () => {
const pkg = {
...testResponse[0],
name: 'elasticsearch',
icons: [{ type: 'image/jpg', src: '/img_nginx.svg', path: '' }],
};
const { icon } = toSearchResult(pkg, startMock.application, startMock.http.basePath);
expect(icon).toMatchInlineSnapshot(`"logoElasticsearch"`);
});
});
}); });

View file

@ -4,21 +4,23 @@
* 2.0; you may not use this file except in compliance with the Elastic License * 2.0; you may not use this file except in compliance with the Elastic License
* 2.0. * 2.0.
*/ */
import type { CoreSetup, CoreStart, ApplicationStart } from 'src/core/public'; import type { CoreSetup, CoreStart, ApplicationStart, IBasePath } from 'src/core/public';
import type { Observable } from 'rxjs'; import type { Observable } from 'rxjs';
import { from, of, combineLatest } from 'rxjs'; import { from, of, combineLatest } from 'rxjs';
import { map, shareReplay, takeUntil } from 'rxjs/operators'; import { map, shareReplay, takeUntil } from 'rxjs/operators';
import { ICON_TYPES } from '@elastic/eui';
import type { import type {
GlobalSearchResultProvider, GlobalSearchResultProvider,
GlobalSearchProviderResult, GlobalSearchProviderResult,
} from '../../global_search/public'; } from '../../global_search/public';
import { INTEGRATIONS_PLUGIN_ID } from '../common'; import { epmRouteService, INTEGRATIONS_PLUGIN_ID } from '../common';
import { sendGetPackages } from './hooks'; import { sendGetPackages } from './hooks';
import type { GetPackagesResponse } from './types'; import type { GetPackagesResponse, PackageListItem } from './types';
import { pagePathGetters } from './constants'; import { pagePathGetters } from './constants';
const packageType = 'integration'; const packageType = 'integration';
@ -34,16 +36,31 @@ const createPackages$ = () =>
shareReplay(1) shareReplay(1)
); );
const toSearchResult = ( const getEuiIconType = (pkg: PackageListItem, basePath: IBasePath): string | undefined => {
pkg: GetPackagesResponse['response'][number], const pkgIcon = pkg.icons?.find((icon) => icon.type === 'image/svg+xml');
application: ApplicationStart if (!pkgIcon) {
) => { // If no valid SVG is available, attempt to fallback to built-in EUI icons
return ICON_TYPES.find((key) => key.toLowerCase() === `logo${pkg.name}`);
}
return basePath.prepend(
epmRouteService.getFilePath(`/package/${pkg.name}/${pkg.version}${pkgIcon.src}`)
);
};
/** Exported for testing only @internal */
export const toSearchResult = (
pkg: PackageListItem,
application: ApplicationStart,
basePath: IBasePath
): GlobalSearchProviderResult => {
const pkgkey = `${pkg.name}-${pkg.version}`; const pkgkey = `${pkg.name}-${pkg.version}`;
return { return {
id: pkgkey, id: pkgkey,
type: packageType, type: packageType,
title: pkg.title, title: pkg.title,
score: 80, score: 80,
icon: getEuiIconType(pkg, basePath),
url: { url: {
// prettier-ignore // prettier-ignore
path: `${application.getUrlForApp(INTEGRATIONS_PLUGIN_ID)}${pagePathGetters.integration_details_overview({ pkgkey })[1]}`, path: `${application.getUrlForApp(INTEGRATIONS_PLUGIN_ID)}${pagePathGetters.integration_details_overview({ pkgkey })[1]}`,
@ -95,13 +112,13 @@ export const createPackageSearchProvider = (core: CoreSetup): GlobalSearchResult
return packagesResponse return packagesResponse
.flatMap( .flatMap(
includeAllPackages includeAllPackages
? (pkg) => toSearchResult(pkg, coreStart.application) ? (pkg) => toSearchResult(pkg, coreStart.application, coreStart.http.basePath)
: (pkg) => { : (pkg) => {
if (!term || !pkg.title.toLowerCase().includes(term)) { if (!term || !pkg.title.toLowerCase().includes(term)) {
return []; return [];
} }
return toSearchResult(pkg, coreStart.application); return toSearchResult(pkg, coreStart.application, coreStart.http.basePath);
} }
) )
.slice(0, maxResults); .slice(0, maxResults);

View file

@ -135,8 +135,8 @@ const resultToOption = (
): EuiSelectableTemplateSitewideOption => { ): EuiSelectableTemplateSitewideOption => {
const { id, title, url, icon, type, meta = {} } = result; const { id, title, url, icon, type, meta = {} } = result;
const { tagIds = [], categoryLabel = '' } = meta as { tagIds: string[]; categoryLabel: string }; const { tagIds = [], categoryLabel = '' } = meta as { tagIds: string[]; categoryLabel: string };
// only displaying icons for applications // only displaying icons for applications and integrations
const useIcon = type === 'application'; const useIcon = type === 'application' || type === 'integration';
const option: EuiSelectableTemplateSitewideOption = { const option: EuiSelectableTemplateSitewideOption = {
key: id, key: id,
label: title, label: title,