Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,78 +3,12 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useEffect, useMemo, useState } from 'react';
import { ICON_TYPES, EuiIcon, EuiIconProps } from '@elastic/eui';
import { PackageInfo, PackageListItem } from '../../../../common/types/models';
import { useLinks } from '../sections/epm/hooks';
import { epmRouteService } from '../../../../common/services';
import { sendRequest } from '../hooks/use_request';
import { GetInfoResponse } from '../types';
type Package = PackageInfo | PackageListItem;
import React from 'react';
import { EuiIcon, EuiIconProps } from '@elastic/eui';
import { usePackageIconType, UsePackageIconType } from '../hooks';

const CACHED_ICONS = new Map<string, string>();

export const PackageIcon: React.FunctionComponent<{
packageName: string;
version?: string;
icons?: Package['icons'];
} & Omit<EuiIconProps, 'type'>> = ({ packageName, version, icons, ...euiIconProps }) => {
const iconType = usePackageIcon(packageName, version, icons);
export const PackageIcon: React.FunctionComponent<UsePackageIconType &
Omit<EuiIconProps, 'type'>> = ({ packageName, version, icons, tryApi, ...euiIconProps }) => {
const iconType = usePackageIconType({ packageName, version, icons, tryApi });
return <EuiIcon size="s" type={iconType} {...euiIconProps} />;
};

const usePackageIcon = (packageName: string, version?: string, icons?: Package['icons']) => {
const { toImage } = useLinks();
const [iconType, setIconType] = useState<string>(''); // FIXME: use `empty` icon during initialization - see: https://github.com/elastic/kibana/issues/60622
const pkgKey = `${packageName}-${version ?? ''}`;

// Generates an icon path or Eui Icon name based on an icon list from the package
// or by using the package name against logo icons from Eui
const fromInput = useMemo(() => {
return (iconList?: Package['icons']) => {
const svgIcons = iconList?.filter(iconDef => iconDef.type === 'image/svg+xml');
const localIconSrc = Array.isArray(svgIcons) && svgIcons[0]?.src;
if (localIconSrc) {
CACHED_ICONS.set(pkgKey, toImage(localIconSrc));
setIconType(CACHED_ICONS.get(pkgKey) as string);
return;
}

const euiLogoIcon = ICON_TYPES.find(key => key.toLowerCase() === `logo${packageName}`);
if (euiLogoIcon) {
CACHED_ICONS.set(pkgKey, euiLogoIcon);
setIconType(euiLogoIcon);
return;
}

CACHED_ICONS.set(pkgKey, 'package');
setIconType('package');
};
}, [packageName, pkgKey, toImage]);

useEffect(() => {
if (CACHED_ICONS.has(pkgKey)) {
setIconType(CACHED_ICONS.get(pkgKey) as string);
return;
}

// Use API to see if package has icons defined
if (!icons && version) {
fromPackageInfo(pkgKey)
.catch(() => undefined) // ignore API errors
.then(fromInput);
} else {
fromInput(icons);
}
}, [icons, toImage, packageName, version, fromInput, pkgKey]);

return iconType;
};

const fromPackageInfo = async (pkgKey: string) => {
const { data } = await sendRequest<GetInfoResponse>({
path: epmRouteService.getInfoPath(pkgKey),
method: 'get',
});
return data?.response?.icons;
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export { useCore, CoreContext } from './use_core';
export { useConfig, ConfigContext } from './use_config';
export { useSetupDeps, useStartDeps, DepsContext } from './use_deps';
export { useLink } from './use_link';
export { usePackageIconType, UsePackageIconType } from './use_package_icon_type';
export { usePagination, Pagination } from './use_pagination';
export { useDebounce } from './use_debounce';
export * from './use_request';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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.
*/

import { useEffect, useState } from 'react';
import { ICON_TYPES } from '@elastic/eui';
import { PackageInfo, PackageListItem } from '../../../../common/types/models';
import { useLinks } from '../sections/epm/hooks';
import { sendGetPackageInfoByKey } from './index';

type Package = PackageInfo | PackageListItem;

export interface UsePackageIconType {
packageName: Package['name'];
version: Package['version'];
icons?: Package['icons'];
tryApi?: boolean; // should it call API to try to find missing icons?
}

const CACHED_ICONS = new Map<string, string>();

export const usePackageIconType = ({
packageName,
version,
icons: paramIcons,
tryApi = false,
}: UsePackageIconType) => {
const { toImage } = useLinks();
const [iconList, setIconList] = useState<UsePackageIconType['icons']>();
const [iconType, setIconType] = useState<string>(''); // FIXME: use `empty` icon during initialization - see: https://github.com/elastic/kibana/issues/60622
const pkgKey = `${packageName}-${version}`;

// Generates an icon path or Eui Icon name based on an icon list from the package
// or by using the package name against logo icons from Eui
useEffect(() => {
if (CACHED_ICONS.has(pkgKey)) {
setIconType(CACHED_ICONS.get(pkgKey) || '');
return;
}
const svgIcons = (paramIcons || iconList)?.filter(iconDef => iconDef.type === 'image/svg+xml');
const localIconSrc = Array.isArray(svgIcons) && svgIcons[0]?.src;
if (localIconSrc) {
CACHED_ICONS.set(pkgKey, toImage(localIconSrc));
setIconType(CACHED_ICONS.get(pkgKey) || '');
return;
}

const euiLogoIcon = ICON_TYPES.find(key => key.toLowerCase() === `logo${packageName}`);
if (euiLogoIcon) {
CACHED_ICONS.set(pkgKey, euiLogoIcon);
setIconType(euiLogoIcon);
return;
}

if (tryApi && !paramIcons && !iconList) {
sendGetPackageInfoByKey(pkgKey)
.catch(error => undefined) // Ignore API errors
.then(res => {
CACHED_ICONS.delete(pkgKey);
setIconList(res?.data?.response?.icons);
});
}

CACHED_ICONS.set(pkgKey, 'package');
setIconType('package');
}, [paramIcons, pkgKey, toImage, iconList, packageName, iconType, tryApi]);

return iconType;
};
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,15 @@ export const StepSelectPackage: React.FunctionComponent<{
return {
label: title || name,
key: pkgkey,
prepend: <PackageIcon packageName={name} version={version} icons={icons} size="m" />,
prepend: (
<PackageIcon
packageName={name}
version={version}
icons={icons}
size="m"
tryApi={true}
/>
),
checked: selectedPkgKey === pkgkey ? 'on' : undefined,
};
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ export const DatasourcesTable: React.FunctionComponent<Props> = ({
packageName={datasource.package.name}
version={datasource.package.version}
size="m"
tryApi={true}
/>
</EuiFlexItem>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export function IconPanel({ iconType }: { iconType: IconType }) {
text-align: center;
vertical-align: middle;
padding: ${props => props.theme.eui.spacerSizes.xl};
svg {
svg,
img {
height: ${props => props.theme.eui.euiKeyPadMenuSize};
width: ${props => props.theme.eui.euiKeyPadMenuSize};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiPage, EuiPageBody, EuiPageProps, ICON_TYPES } from '@elastic/eui';
import { EuiPage, EuiPageBody, EuiPageProps } from '@elastic/eui';
import React, { Fragment, useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import styled from 'styled-components';
Expand All @@ -12,7 +12,7 @@ import { PackageInfo } from '../../../../types';
import { useSetPackageInstallStatus } from '../../hooks';
import { Content } from './content';
import { Header } from './header';
import { sendGetPackageInfoByKey } from '../../../../hooks';
import { sendGetPackageInfoByKey, usePackageIconType } from '../../../../hooks';

export const DEFAULT_PANEL: DetailViewPanelName = 'overview';

Expand Down Expand Up @@ -62,8 +62,8 @@ const FullWidthContent = styled(EuiPage)`

type LayoutProps = PackageInfo & Pick<DetailParams, 'panel'> & Pick<EuiPageProps, 'restrictWidth'>;
export function DetailLayout(props: LayoutProps) {
const { name, restrictWidth } = props;
const iconType = ICON_TYPES.find(key => key.toLowerCase() === `logo${name}`);
const { name: packageName, version, icons, restrictWidth } = props;
const iconType = usePackageIconType({ packageName, version, icons });

return (
<Fragment>
Expand Down