[Fleet] Add icons to Integrations global search results (#111131)
This commit is contained in:
parent
51fd4abe20
commit
099a63ea2a
|
@ -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"`);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue