From 7eeacc92528656806c26a489136a3b58ccc968de Mon Sep 17 00:00:00 2001 From: John Schulz Date: Tue, 31 Mar 2020 16:25:47 -0400 Subject: [PATCH 1/3] EPM detail page now uses same icon as list page. Export the hook used by PackageIcon component. Removed HTTP API call. `icons` comes from the API, so we don't want to call the API again. --- .../components/package_icon.tsx | 38 +++++++++---------- .../sections/epm/components/icon_panel.tsx | 3 +- .../sections/epm/screens/detail/index.tsx | 7 ++-- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/package_icon.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/package_icon.tsx index c5a0e600b7d50..b758a4e7d8eb5 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/package_icon.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/package_icon.tsx @@ -7,9 +7,6 @@ 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; const CACHED_ICONS = new Map(); @@ -19,14 +16,25 @@ export const PackageIcon: React.FunctionComponent<{ version?: string; icons?: Package['icons']; } & Omit> = ({ packageName, version, icons, ...euiIconProps }) => { - const iconType = usePackageIcon(packageName, version, icons); + const iconType = usePackageIconType({ packageName, version, icons }); return ; }; -const usePackageIcon = (packageName: string, version?: string, icons?: Package['icons']) => { +interface UsePackageIconType { + packageName: string; + version?: string; + icons?: Package['icons']; +} + +export const usePackageIconType = ({ + packageName = '', + version = '', + icons = [], +}: UsePackageIconType) => { const { toImage } = useLinks(); const [iconType, setIconType] = useState(''); // FIXME: use `empty` icon during initialization - see: https://github.com/elastic/kibana/issues/60622 - const pkgKey = `${packageName}-${version ?? ''}`; + const pkgKey = `${packageName}-${version}`; + const defaultType = 'package'; // 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 @@ -47,8 +55,8 @@ const usePackageIcon = (packageName: string, version?: string, icons?: Package[' return; } - CACHED_ICONS.set(pkgKey, 'package'); - setIconType('package'); + CACHED_ICONS.set(pkgKey, defaultType); + setIconType(defaultType); }; }, [packageName, pkgKey, toImage]); @@ -59,22 +67,10 @@ const usePackageIcon = (packageName: string, version?: string, icons?: Package[' } // Use API to see if package has icons defined - if (!icons && version !== undefined) { - fromPackageInfo(pkgKey) - .catch(() => undefined) // ignore API errors - .then(fromInput); - } else { + if (icons) { fromInput(icons); } }, [icons, toImage, packageName, version, fromInput, pkgKey]); return iconType; }; - -const fromPackageInfo = async (pkgKey: string) => { - const { data } = await sendRequest({ - path: epmRouteService.getInfoPath(pkgKey), - method: 'get', - }); - return data?.response?.icons; -}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/icon_panel.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/icon_panel.tsx index 7ce386ed56f5f..684b158b5da86 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/icon_panel.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/icon_panel.tsx @@ -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}; } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx index 4bc90c6a0f8fd..7b29bb0232d67 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx @@ -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'; @@ -12,6 +12,7 @@ import { PackageInfo } from '../../../../types'; import { useSetPackageInstallStatus } from '../../hooks'; import { Content } from './content'; import { Header } from './header'; +import { usePackageIconType } from '../../../../components/package_icon'; import { sendGetPackageInfoByKey } from '../../../../hooks'; export const DEFAULT_PANEL: DetailViewPanelName = 'overview'; @@ -62,8 +63,8 @@ const FullWidthContent = styled(EuiPage)` type LayoutProps = PackageInfo & Pick & Pick; 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 ( From 0a0a319f2e15faaa94d88f0087ff041fa0b6f784 Mon Sep 17 00:00:00 2001 From: John Schulz Date: Tue, 31 Mar 2020 16:29:52 -0400 Subject: [PATCH 2/3] Moved hook from ./components to ./hooks --- .../components/package_icon.tsx | 63 +---------------- .../ingest_manager/hooks/index.ts | 1 + .../hooks/use_package_icon_type.ts | 68 +++++++++++++++++++ .../sections/epm/screens/detail/index.tsx | 3 +- 4 files changed, 73 insertions(+), 62 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_package_icon_type.ts diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/package_icon.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/package_icon.tsx index b758a4e7d8eb5..a1dfe7e505b1c 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/package_icon.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/package_icon.tsx @@ -3,14 +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 React from 'react'; +import { EuiIcon, EuiIconProps } from '@elastic/eui'; import { PackageInfo, PackageListItem } from '../../../../common/types/models'; -import { useLinks } from '../sections/epm/hooks'; +import { usePackageIconType } from '../hooks'; type Package = PackageInfo | PackageListItem; -const CACHED_ICONS = new Map(); - export const PackageIcon: React.FunctionComponent<{ packageName: string; version?: string; @@ -19,58 +17,3 @@ export const PackageIcon: React.FunctionComponent<{ const iconType = usePackageIconType({ packageName, version, icons }); return ; }; - -interface UsePackageIconType { - packageName: string; - version?: string; - icons?: Package['icons']; -} - -export const usePackageIconType = ({ - packageName = '', - version = '', - icons = [], -}: UsePackageIconType) => { - const { toImage } = useLinks(); - const [iconType, setIconType] = useState(''); // FIXME: use `empty` icon during initialization - see: https://github.com/elastic/kibana/issues/60622 - const pkgKey = `${packageName}-${version}`; - const defaultType = 'package'; - - // 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, defaultType); - setIconType(defaultType); - }; - }, [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) { - fromInput(icons); - } - }, [icons, toImage, packageName, version, fromInput, pkgKey]); - - return iconType; -}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts index 5e0695bd3e305..f28601ff55026 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts @@ -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 } from './use_package_icon_type'; export { usePagination, Pagination } from './use_pagination'; export { useDebounce } from './use_debounce'; export * from './use_request'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_package_icon_type.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_package_icon_type.ts new file mode 100644 index 0000000000000..ba4af3f3d5d88 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_package_icon_type.ts @@ -0,0 +1,68 @@ +/* + * 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, useMemo, useState } from 'react'; +import { ICON_TYPES } from '@elastic/eui'; +import { PackageInfo, PackageListItem } from '../../../../common/types/models'; +import { useLinks } from '../sections/epm/hooks'; + +type Package = PackageInfo | PackageListItem; + +export interface UsePackageIconType { + packageName: Package['name']; + version?: Package['version']; + icons?: Package['icons']; +} + +const CACHED_ICONS = new Map(); + +export const usePackageIconType = ({ + packageName = '', + version = '', + icons = [], +}: UsePackageIconType) => { + const { toImage } = useLinks(); + const [iconType, setIconType] = useState(''); // FIXME: use `empty` icon during initialization - see: https://github.com/elastic/kibana/issues/60622 + const pkgKey = `${packageName}-${version}`; + const defaultType = 'package'; + + // 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, defaultType); + setIconType(defaultType); + }; + }, [packageName, pkgKey, toImage]); + + useEffect(() => { + if (CACHED_ICONS.has(pkgKey)) { + setIconType(CACHED_ICONS.get(pkgKey) || ''); + return; + } + + if (icons) { + fromInput(icons); + } + }, [icons, toImage, packageName, version, fromInput, pkgKey, iconType]); + + return iconType; +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx index 7b29bb0232d67..3239d7b90e3c3 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx @@ -12,8 +12,7 @@ import { PackageInfo } from '../../../../types'; import { useSetPackageInstallStatus } from '../../hooks'; import { Content } from './content'; import { Header } from './header'; -import { usePackageIconType } from '../../../../components/package_icon'; -import { sendGetPackageInfoByKey } from '../../../../hooks'; +import { sendGetPackageInfoByKey, usePackageIconType } from '../../../../hooks'; export const DEFAULT_PANEL: DetailViewPanelName = 'overview'; From 5c3b0cdb1bce27ed146c504eeb0e9e85a36bf78e Mon Sep 17 00:00:00 2001 From: John Schulz Date: Thu, 2 Apr 2020 17:35:58 -0400 Subject: [PATCH 3/3] Add optional `tryApi` param to PackageIcon. Trusts given values by default but can opt-in to side-effects like calling API. Prevents trying API when we know none are present (api explicitly returns none). This also fixes the issue in master where the API was called several times for each item in the datasources list. --- .../components/package_icon.tsx | 13 ++-- .../ingest_manager/hooks/index.ts | 2 +- .../hooks/use_package_icon_type.ts | 65 ++++++++++--------- .../step_select_package.tsx | 10 ++- .../datasources/datasources_table.tsx | 1 + 5 files changed, 49 insertions(+), 42 deletions(-) diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/package_icon.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/package_icon.tsx index a1dfe7e505b1c..de0dd75f635cf 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/package_icon.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/package_icon.tsx @@ -5,15 +5,10 @@ */ import React from 'react'; import { EuiIcon, EuiIconProps } from '@elastic/eui'; -import { PackageInfo, PackageListItem } from '../../../../common/types/models'; -import { usePackageIconType } from '../hooks'; -type Package = PackageInfo | PackageListItem; +import { usePackageIconType, UsePackageIconType } from '../hooks'; -export const PackageIcon: React.FunctionComponent<{ - packageName: string; - version?: string; - icons?: Package['icons']; -} & Omit> = ({ packageName, version, icons, ...euiIconProps }) => { - const iconType = usePackageIconType({ packageName, version, icons }); +export const PackageIcon: React.FunctionComponent> = ({ packageName, version, icons, tryApi, ...euiIconProps }) => { + const iconType = usePackageIconType({ packageName, version, icons, tryApi }); return ; }; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts index f28601ff55026..66c7333150fb7 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts @@ -9,7 +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 } from './use_package_icon_type'; +export { usePackageIconType, UsePackageIconType } from './use_package_icon_type'; export { usePagination, Pagination } from './use_pagination'; export { useDebounce } from './use_debounce'; export * from './use_request'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_package_icon_type.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_package_icon_type.ts index ba4af3f3d5d88..5f231b5cc9ec9 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_package_icon_type.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_package_icon_type.ts @@ -4,65 +4,68 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useEffect, useMemo, useState } from 'react'; +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']; + version: Package['version']; icons?: Package['icons']; + tryApi?: boolean; // should it call API to try to find missing icons? } const CACHED_ICONS = new Map(); export const usePackageIconType = ({ - packageName = '', - version = '', - icons = [], + packageName, + version, + icons: paramIcons, + tryApi = false, }: UsePackageIconType) => { const { toImage } = useLinks(); + const [iconList, setIconList] = useState(); const [iconType, setIconType] = useState(''); // FIXME: use `empty` icon during initialization - see: https://github.com/elastic/kibana/issues/60622 const pkgKey = `${packageName}-${version}`; - const defaultType = 'package'; // 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, defaultType); - setIconType(defaultType); - }; - }, [packageName, pkgKey, toImage]); - 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; + } - if (icons) { - fromInput(icons); + const euiLogoIcon = ICON_TYPES.find(key => key.toLowerCase() === `logo${packageName}`); + if (euiLogoIcon) { + CACHED_ICONS.set(pkgKey, euiLogoIcon); + setIconType(euiLogoIcon); + return; } - }, [icons, toImage, packageName, version, fromInput, pkgKey, iconType]); + + 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; }; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_select_package.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_select_package.tsx index 0b48020c3cac1..cc7fc89ab8a80 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_select_package.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_select_package.tsx @@ -130,7 +130,15 @@ export const StepSelectPackage: React.FunctionComponent<{ return { label: title || name, key: pkgkey, - prepend: , + prepend: ( + + ), checked: selectedPkgKey === pkgkey ? 'on' : undefined, }; })} diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/datasources/datasources_table.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/datasources/datasources_table.tsx index 49285707457e1..87155afdc21be 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/datasources/datasources_table.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/datasources/datasources_table.tsx @@ -150,6 +150,7 @@ export const DatasourcesTable: React.FunctionComponent = ({ packageName={datasource.package.name} version={datasource.package.version} size="m" + tryApi={true} /> )}