mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Fleet] Add a visual indication of selected subcategory in Integrations page (#149954)
Closes https://github.com/elastic/kibana/issues/149306 ## Summary Display a clear indication of selected subcategory in Integrations page https://user-images.githubusercontent.com/16084106/215807007-63dbea8d-4496-497f-b4f4-673825a21049.mov To test it locally, enable feature flag `showIntegrationsSubcategories`. Some screenshots: <img width="2040" alt="Screenshot 2023-01-31 at 16 12 35" src="https://user-images.githubusercontent.com/16084106/215807361-382eb4fa-736c-4073-bf44-79d1d9a3109c.png"> <img width="1563" alt="Screenshot 2023-01-31 at 16 36 38" src="https://user-images.githubusercontent.com/16084106/215807406-f7d52c44-d1d1-4f4a-b32a-26122ab8cfbe.png"> <img width="1507" alt="Screenshot 2023-01-31 at 16 36 51" src="https://user-images.githubusercontent.com/16084106/215807430-00189482-2dd3-418c-99b9-0651b82305b7.png"> I also split some of the components in `packageList` since that file is becoming too big and extracted another hook from `useAvailablePackages`, this hook only deals with the URL and the history. --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
612b8e7d8a
commit
c8c27d7def
7 changed files with 667 additions and 522 deletions
|
@ -1,469 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ReactNode, FunctionComponent } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
import {
|
||||
EuiFlexGrid,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiLink,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
EuiFieldSearch,
|
||||
EuiText,
|
||||
useEuiTheme,
|
||||
EuiIcon,
|
||||
EuiScreenReaderOnly,
|
||||
EuiButton,
|
||||
EuiButtonIcon,
|
||||
EuiPopover,
|
||||
EuiContextMenuPanel,
|
||||
EuiContextMenuItem,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { Loading } from '../../../components';
|
||||
import { useLocalSearch, searchIdField } from '../../../hooks';
|
||||
|
||||
import type { IntegrationCardItem } from '../../../../../../common/types/models';
|
||||
|
||||
import type { ExtendedIntegrationCategory, CategoryFacet } from '../screens/home/category_facets';
|
||||
|
||||
import type { IntegrationsURLParameters } from '../screens/home/hooks/use_available_packages';
|
||||
|
||||
import { ExperimentalFeaturesService } from '../../../services';
|
||||
|
||||
import { promoteFeaturedIntegrations } from './utils';
|
||||
|
||||
import { PackageCard } from './package_card';
|
||||
|
||||
export interface Props {
|
||||
isLoading?: boolean;
|
||||
controls?: ReactNode | ReactNode[];
|
||||
list: IntegrationCardItem[];
|
||||
searchTerm: string;
|
||||
setSearchTerm: (search: string) => void;
|
||||
selectedCategory: ExtendedIntegrationCategory;
|
||||
setCategory: (category: ExtendedIntegrationCategory) => void;
|
||||
categories: CategoryFacet[];
|
||||
setUrlandReplaceHistory: (params: IntegrationsURLParameters) => void;
|
||||
setUrlandPushHistory: (params: IntegrationsURLParameters) => void;
|
||||
callout?: JSX.Element | null;
|
||||
// Props used only in AvailablePackages component:
|
||||
showCardLabels?: boolean;
|
||||
title?: string;
|
||||
availableSubCategories?: CategoryFacet[];
|
||||
selectedSubCategory?: string;
|
||||
setSelectedSubCategory?: (c: string | undefined) => void;
|
||||
showMissingIntegrationMessage?: boolean;
|
||||
}
|
||||
|
||||
export const PackageListGrid: FunctionComponent<Props> = ({
|
||||
isLoading,
|
||||
controls,
|
||||
title,
|
||||
list,
|
||||
searchTerm,
|
||||
setSearchTerm,
|
||||
selectedCategory,
|
||||
setCategory,
|
||||
categories,
|
||||
availableSubCategories,
|
||||
setSelectedSubCategory,
|
||||
selectedSubCategory,
|
||||
setUrlandReplaceHistory,
|
||||
setUrlandPushHistory,
|
||||
showMissingIntegrationMessage = false,
|
||||
callout,
|
||||
showCardLabels = true,
|
||||
}) => {
|
||||
const localSearchRef = useLocalSearch(list);
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
const [isPopoverOpen, setPopover] = useState(false);
|
||||
|
||||
const MAX_SUBCATEGORIES_NUMBER = 6;
|
||||
|
||||
const { showIntegrationsSubcategories } = ExperimentalFeaturesService.get();
|
||||
|
||||
const onButtonClick = () => {
|
||||
setPopover(!isPopoverOpen);
|
||||
};
|
||||
|
||||
const closePopover = () => {
|
||||
setPopover(false);
|
||||
};
|
||||
|
||||
const onQueryChange = (e: any) => {
|
||||
const queryText = e.target.value;
|
||||
setSearchTerm(queryText);
|
||||
setUrlandReplaceHistory({
|
||||
searchString: queryText,
|
||||
categoryId: selectedCategory,
|
||||
subCategoryId: selectedSubCategory,
|
||||
});
|
||||
};
|
||||
|
||||
const resetQuery = () => {
|
||||
setSearchTerm('');
|
||||
setUrlandReplaceHistory({ searchString: '', categoryId: '', subCategoryId: '' });
|
||||
};
|
||||
|
||||
const onSubCategoryClick = useCallback(
|
||||
(subCategory: string) => {
|
||||
if (setSelectedSubCategory) setSelectedSubCategory(subCategory);
|
||||
setUrlandPushHistory({
|
||||
categoryId: selectedCategory,
|
||||
subCategoryId: subCategory,
|
||||
});
|
||||
},
|
||||
[selectedCategory, setSelectedSubCategory, setUrlandPushHistory]
|
||||
);
|
||||
|
||||
const selectedCategoryTitle = selectedCategory
|
||||
? categories.find((category) => category.id === selectedCategory)?.title
|
||||
: undefined;
|
||||
|
||||
const filteredPromotedList = useMemo(() => {
|
||||
if (isLoading) return [];
|
||||
const filteredList = searchTerm
|
||||
? list.filter((item) =>
|
||||
(localSearchRef.current!.search(searchTerm) as IntegrationCardItem[])
|
||||
.map((match) => match[searchIdField])
|
||||
.includes(item[searchIdField])
|
||||
)
|
||||
: list;
|
||||
|
||||
return promoteFeaturedIntegrations(filteredList, selectedCategory);
|
||||
}, [isLoading, list, localSearchRef, searchTerm, selectedCategory]);
|
||||
|
||||
const splitSubcategories = (
|
||||
subcategories: CategoryFacet[] | undefined
|
||||
): { visibleSubCategories?: CategoryFacet[]; hiddenSubCategories?: CategoryFacet[] } => {
|
||||
if (!subcategories) return {};
|
||||
else if (subcategories && subcategories?.length < MAX_SUBCATEGORIES_NUMBER) {
|
||||
return { visibleSubCategories: subcategories, hiddenSubCategories: [] };
|
||||
} else if (subcategories && subcategories?.length >= MAX_SUBCATEGORIES_NUMBER) {
|
||||
return {
|
||||
visibleSubCategories: subcategories.slice(0, MAX_SUBCATEGORIES_NUMBER),
|
||||
hiddenSubCategories: subcategories.slice(MAX_SUBCATEGORIES_NUMBER),
|
||||
};
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
const splitSubcat = splitSubcategories(availableSubCategories);
|
||||
const { visibleSubCategories } = splitSubcat;
|
||||
const hiddenSubCategoriesItems = useMemo(() => {
|
||||
return splitSubcat?.hiddenSubCategories?.map((subCategory) => {
|
||||
return (
|
||||
<EuiContextMenuItem
|
||||
key={subCategory.id}
|
||||
onClick={() => {
|
||||
onSubCategoryClick(subCategory.id);
|
||||
closePopover();
|
||||
}}
|
||||
>
|
||||
{subCategory.title}
|
||||
</EuiContextMenuItem>
|
||||
);
|
||||
});
|
||||
}, [onSubCategoryClick, splitSubcat.hiddenSubCategories]);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup alignItems="flexStart" gutterSize="xl" data-test-subj="epmList.integrationCards">
|
||||
<EuiFlexItem
|
||||
data-test-subj="epmList.controlsSideColumn"
|
||||
grow={1}
|
||||
style={{
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
zIndex: 100,
|
||||
}}
|
||||
>
|
||||
<ControlsColumn controls={controls} title={title} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={5} data-test-subj="epmList.mainColumn">
|
||||
<EuiFieldSearch
|
||||
data-test-subj="epmList.searchBar"
|
||||
placeholder={i18n.translate('xpack.fleet.epmList.searchPackagesPlaceholder', {
|
||||
defaultMessage: 'Search for integrations',
|
||||
})}
|
||||
value={searchTerm}
|
||||
onChange={(e) => onQueryChange(e)}
|
||||
isClearable={true}
|
||||
incremental={true}
|
||||
fullWidth={true}
|
||||
prepend={
|
||||
selectedCategoryTitle ? (
|
||||
<EuiText
|
||||
data-test-subj="epmList.categoryBadge"
|
||||
size="xs"
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
fontWeight: euiTheme.font.weight.bold,
|
||||
backgroundColor: euiTheme.colors.lightestShade,
|
||||
}}
|
||||
>
|
||||
<EuiScreenReaderOnly>
|
||||
<span>Searching category: </span>
|
||||
</EuiScreenReaderOnly>
|
||||
{selectedCategoryTitle}
|
||||
<button
|
||||
data-test-subj="epmList.categoryBadge.closeBtn"
|
||||
onClick={() => {
|
||||
setCategory('');
|
||||
if (setSelectedSubCategory) setSelectedSubCategory(undefined);
|
||||
setUrlandReplaceHistory({
|
||||
searchString: '',
|
||||
categoryId: '',
|
||||
subCategoryId: '',
|
||||
});
|
||||
}}
|
||||
aria-label="Remove filter"
|
||||
style={{
|
||||
padding: euiTheme.size.xs,
|
||||
paddingTop: '2px',
|
||||
}}
|
||||
>
|
||||
<EuiIcon
|
||||
type="cross"
|
||||
color="text"
|
||||
size="s"
|
||||
style={{
|
||||
width: 'auto',
|
||||
padding: 0,
|
||||
backgroundColor: euiTheme.colors.lightestShade,
|
||||
}}
|
||||
/>
|
||||
</button>
|
||||
</EuiText>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
{showIntegrationsSubcategories && availableSubCategories?.length ? <EuiSpacer /> : null}
|
||||
{showIntegrationsSubcategories ? (
|
||||
<EuiFlexGroup
|
||||
data-test-subj="epmList.subcategoriesRow"
|
||||
justifyContent="flexStart"
|
||||
direction="row"
|
||||
gutterSize="s"
|
||||
style={{
|
||||
maxWidth: 943,
|
||||
}}
|
||||
>
|
||||
{visibleSubCategories?.map((subCategory) => (
|
||||
<EuiFlexItem grow={false} key={subCategory.id}>
|
||||
<EuiButton
|
||||
color="text"
|
||||
aria-label={subCategory?.title}
|
||||
onClick={() => onSubCategoryClick(subCategory.id)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.epmList.subcategoriesButton"
|
||||
defaultMessage="{subcategory}"
|
||||
values={{
|
||||
subcategory: subCategory.title,
|
||||
}}
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
{hiddenSubCategoriesItems?.length ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiPopover
|
||||
data-test-subj="epmList.showMoreSubCategoriesButton"
|
||||
id="moreSubCategories"
|
||||
button={
|
||||
<EuiButtonIcon
|
||||
display="base"
|
||||
onClick={onButtonClick}
|
||||
iconType="boxesHorizontal"
|
||||
aria-label="Show more subcategories"
|
||||
size="m"
|
||||
/>
|
||||
}
|
||||
isOpen={isPopoverOpen}
|
||||
closePopover={closePopover}
|
||||
panelPaddingSize="none"
|
||||
anchorPosition="downLeft"
|
||||
>
|
||||
<EuiContextMenuPanel size="s" items={hiddenSubCategoriesItems} />
|
||||
</EuiPopover>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
) : null}
|
||||
{callout ? (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
{callout}
|
||||
</>
|
||||
) : null}
|
||||
<EuiSpacer />
|
||||
<GridColumn
|
||||
isLoading={isLoading || !localSearchRef.current}
|
||||
list={filteredPromotedList}
|
||||
showMissingIntegrationMessage={showMissingIntegrationMessage}
|
||||
showCardLabels={showCardLabels}
|
||||
/>
|
||||
{showMissingIntegrationMessage && (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
<MissingIntegrationContent
|
||||
setUrlandPushHistory={setUrlandPushHistory}
|
||||
resetQuery={resetQuery}
|
||||
setSelectedCategory={setCategory}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
||||
interface ControlsColumnProps {
|
||||
controls: ReactNode;
|
||||
title: string | undefined;
|
||||
}
|
||||
|
||||
const ControlsColumn = ({ controls, title }: ControlsColumnProps) => {
|
||||
let titleContent;
|
||||
if (title) {
|
||||
titleContent = (
|
||||
<>
|
||||
<EuiTitle size="s">
|
||||
<h2>{title}</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="l" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<EuiFlexGroup direction="column" gutterSize="none">
|
||||
{titleContent}
|
||||
{controls}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
||||
interface GridColumnProps {
|
||||
list: IntegrationCardItem[];
|
||||
isLoading: boolean;
|
||||
showMissingIntegrationMessage?: boolean;
|
||||
showCardLabels?: boolean;
|
||||
}
|
||||
|
||||
const GridColumn = ({
|
||||
list,
|
||||
showMissingIntegrationMessage = false,
|
||||
showCardLabels = false,
|
||||
isLoading,
|
||||
}: GridColumnProps) => {
|
||||
if (isLoading) return <Loading />;
|
||||
|
||||
return (
|
||||
<EuiFlexGrid gutterSize="l" columns={3}>
|
||||
{list.length ? (
|
||||
list.map((item) => {
|
||||
return (
|
||||
<EuiFlexItem
|
||||
key={item.id}
|
||||
// Ensure that cards wrapped in EuiTours/EuiPopovers correctly inherit the full grid row height
|
||||
css={css`
|
||||
& > .euiPopover,
|
||||
& > .euiPopover > .euiPopover__anchor,
|
||||
& > .euiPopover > .euiPopover__anchor > .euiCard {
|
||||
height: 100%;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<PackageCard {...item} showLabels={showCardLabels} />
|
||||
</EuiFlexItem>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<EuiFlexItem grow={3}>
|
||||
<EuiText>
|
||||
<p>
|
||||
{showMissingIntegrationMessage ? (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.epmList.missingIntegrationPlaceholder"
|
||||
defaultMessage="We didn't find any integrations matching your search term. Please try another keyword or browse using the categories on the left."
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.epmList.noPackagesFoundPlaceholder"
|
||||
defaultMessage="No integrations found"
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGrid>
|
||||
);
|
||||
};
|
||||
|
||||
interface MissingIntegrationContentProps {
|
||||
resetQuery: () => void;
|
||||
setSelectedCategory: (category: ExtendedIntegrationCategory) => void;
|
||||
setUrlandPushHistory: (params: IntegrationsURLParameters) => void;
|
||||
}
|
||||
|
||||
const MissingIntegrationContent = ({
|
||||
resetQuery,
|
||||
setSelectedCategory,
|
||||
setUrlandPushHistory,
|
||||
}: MissingIntegrationContentProps) => {
|
||||
const handleCustomInputsLinkClick = useCallback(() => {
|
||||
resetQuery();
|
||||
setSelectedCategory('custom');
|
||||
setUrlandPushHistory({
|
||||
categoryId: 'custom',
|
||||
subCategoryId: '',
|
||||
});
|
||||
}, [resetQuery, setSelectedCategory, setUrlandPushHistory]);
|
||||
|
||||
return (
|
||||
<EuiText size="s" color="subdued">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.integrations.missing"
|
||||
defaultMessage="Don't see an integration? Collect any logs or metrics using our {customInputsLink}. Request new integrations in our {forumLink}."
|
||||
values={{
|
||||
customInputsLink: (
|
||||
<EuiLink onClick={handleCustomInputsLinkClick}>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.integrations.customInputsLink"
|
||||
defaultMessage="custom inputs"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
forumLink: (
|
||||
<EuiLink href="https://discuss.elastic.co/tag/integrations" external target="_blank">
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.integrations.discussForumLink"
|
||||
defaultMessage="forum"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
import {
|
||||
EuiFlexGrid,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiLink,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { Loading } from '../../../../components';
|
||||
|
||||
import type { IntegrationCardItem } from '../../../../../../../common/types/models';
|
||||
|
||||
import type { ExtendedIntegrationCategory } from '../../screens/home/category_facets';
|
||||
|
||||
import type { IntegrationsURLParameters } from '../../screens/home/hooks/use_available_packages';
|
||||
|
||||
import { PackageCard } from '../package_card';
|
||||
|
||||
interface ControlsColumnProps {
|
||||
controls: ReactNode;
|
||||
title: string | undefined;
|
||||
}
|
||||
|
||||
export const ControlsColumn = ({ controls, title }: ControlsColumnProps) => {
|
||||
let titleContent;
|
||||
if (title) {
|
||||
titleContent = (
|
||||
<>
|
||||
<EuiTitle size="s">
|
||||
<h2>{title}</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="l" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<EuiFlexGroup direction="column" gutterSize="none">
|
||||
{titleContent}
|
||||
{controls}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
||||
interface GridColumnProps {
|
||||
list: IntegrationCardItem[];
|
||||
isLoading: boolean;
|
||||
showMissingIntegrationMessage?: boolean;
|
||||
showCardLabels?: boolean;
|
||||
}
|
||||
|
||||
export const GridColumn = ({
|
||||
list,
|
||||
showMissingIntegrationMessage = false,
|
||||
showCardLabels = false,
|
||||
isLoading,
|
||||
}: GridColumnProps) => {
|
||||
if (isLoading) return <Loading />;
|
||||
|
||||
return (
|
||||
<EuiFlexGrid gutterSize="l" columns={3}>
|
||||
{list.length ? (
|
||||
list.map((item) => {
|
||||
return (
|
||||
<EuiFlexItem
|
||||
key={item.id}
|
||||
// Ensure that cards wrapped in EuiTours/EuiPopovers correctly inherit the full grid row height
|
||||
css={css`
|
||||
& > .euiPopover,
|
||||
& > .euiPopover > .euiPopover__anchor,
|
||||
& > .euiPopover > .euiPopover__anchor > .euiCard {
|
||||
height: 100%;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<PackageCard {...item} showLabels={showCardLabels} />
|
||||
</EuiFlexItem>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<EuiFlexItem grow={3}>
|
||||
<EuiText>
|
||||
<p>
|
||||
{showMissingIntegrationMessage ? (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.epmList.missingIntegrationPlaceholder"
|
||||
defaultMessage="We didn't find any integrations matching your search term. Please try another keyword or browse using the categories on the left."
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.epmList.noPackagesFoundPlaceholder"
|
||||
defaultMessage="No integrations found"
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGrid>
|
||||
);
|
||||
};
|
||||
|
||||
interface MissingIntegrationContentProps {
|
||||
resetQuery: () => void;
|
||||
setSelectedCategory: (category: ExtendedIntegrationCategory) => void;
|
||||
setUrlandPushHistory: (params: IntegrationsURLParameters) => void;
|
||||
}
|
||||
|
||||
export const MissingIntegrationContent = ({
|
||||
resetQuery,
|
||||
setSelectedCategory,
|
||||
setUrlandPushHistory,
|
||||
}: MissingIntegrationContentProps) => {
|
||||
const handleCustomInputsLinkClick = useCallback(() => {
|
||||
resetQuery();
|
||||
setSelectedCategory('custom');
|
||||
setUrlandPushHistory({
|
||||
categoryId: 'custom',
|
||||
subCategoryId: '',
|
||||
});
|
||||
}, [resetQuery, setSelectedCategory, setUrlandPushHistory]);
|
||||
|
||||
return (
|
||||
<EuiText size="s" color="subdued">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.integrations.missing"
|
||||
defaultMessage="Don't see an integration? Collect any logs or metrics using our {customInputsLink}. Request new integrations in our {forumLink}."
|
||||
values={{
|
||||
customInputsLink: (
|
||||
<EuiLink onClick={handleCustomInputsLinkClick}>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.integrations.customInputsLink"
|
||||
defaultMessage="custom inputs"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
forumLink: (
|
||||
<EuiLink href="https://discuss.elastic.co/tag/integrations" external target="_blank">
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.integrations.discussForumLink"
|
||||
defaultMessage="forum"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
);
|
||||
};
|
|
@ -9,8 +9,8 @@ import React from 'react';
|
|||
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import type { Props } from './package_list_grid';
|
||||
import { PackageListGrid } from './package_list_grid';
|
||||
import type { Props } from '.';
|
||||
import { PackageListGrid } from '.';
|
||||
|
||||
export default {
|
||||
component: PackageListGrid,
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ReactNode, FunctionComponent } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
import {
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiSpacer,
|
||||
EuiButton,
|
||||
EuiButtonIcon,
|
||||
EuiPopover,
|
||||
EuiContextMenuPanel,
|
||||
EuiContextMenuItem,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { useLocalSearch, searchIdField } from '../../../../hooks';
|
||||
|
||||
import type { IntegrationCardItem } from '../../../../../../../common/types/models';
|
||||
|
||||
import type {
|
||||
ExtendedIntegrationCategory,
|
||||
CategoryFacet,
|
||||
} from '../../screens/home/category_facets';
|
||||
|
||||
import type { IntegrationsURLParameters } from '../../screens/home/hooks/use_available_packages';
|
||||
|
||||
import { ExperimentalFeaturesService } from '../../../../services';
|
||||
|
||||
import { promoteFeaturedIntegrations } from '../utils';
|
||||
|
||||
import { ControlsColumn, MissingIntegrationContent, GridColumn } from './controls';
|
||||
import { SearchBox } from './search_box';
|
||||
|
||||
export interface Props {
|
||||
isLoading?: boolean;
|
||||
controls?: ReactNode | ReactNode[];
|
||||
list: IntegrationCardItem[];
|
||||
searchTerm: string;
|
||||
setSearchTerm: (search: string) => void;
|
||||
selectedCategory: ExtendedIntegrationCategory;
|
||||
setCategory: (category: ExtendedIntegrationCategory) => void;
|
||||
categories: CategoryFacet[];
|
||||
setUrlandReplaceHistory: (params: IntegrationsURLParameters) => void;
|
||||
setUrlandPushHistory: (params: IntegrationsURLParameters) => void;
|
||||
callout?: JSX.Element | null;
|
||||
// Props used only in AvailablePackages component:
|
||||
showCardLabels?: boolean;
|
||||
title?: string;
|
||||
availableSubCategories?: CategoryFacet[];
|
||||
selectedSubCategory?: string;
|
||||
setSelectedSubCategory?: (c: string | undefined) => void;
|
||||
showMissingIntegrationMessage?: boolean;
|
||||
}
|
||||
|
||||
export const PackageListGrid: FunctionComponent<Props> = ({
|
||||
isLoading,
|
||||
controls,
|
||||
title,
|
||||
list,
|
||||
searchTerm,
|
||||
setSearchTerm,
|
||||
selectedCategory,
|
||||
setCategory,
|
||||
categories,
|
||||
availableSubCategories,
|
||||
setSelectedSubCategory,
|
||||
selectedSubCategory,
|
||||
setUrlandReplaceHistory,
|
||||
setUrlandPushHistory,
|
||||
showMissingIntegrationMessage = false,
|
||||
callout,
|
||||
showCardLabels = true,
|
||||
}) => {
|
||||
const localSearchRef = useLocalSearch(list);
|
||||
|
||||
const [isPopoverOpen, setPopover] = useState(false);
|
||||
|
||||
const MAX_SUBCATEGORIES_NUMBER = 6;
|
||||
|
||||
const { showIntegrationsSubcategories } = ExperimentalFeaturesService.get();
|
||||
|
||||
const onButtonClick = () => {
|
||||
setPopover(!isPopoverOpen);
|
||||
};
|
||||
|
||||
const closePopover = () => {
|
||||
setPopover(false);
|
||||
};
|
||||
|
||||
const resetQuery = () => {
|
||||
setSearchTerm('');
|
||||
setUrlandReplaceHistory({ searchString: '', categoryId: '', subCategoryId: '' });
|
||||
};
|
||||
|
||||
const onSubCategoryClick = useCallback(
|
||||
(subCategory: string) => {
|
||||
if (setSelectedSubCategory) setSelectedSubCategory(subCategory);
|
||||
setUrlandPushHistory({
|
||||
categoryId: selectedCategory,
|
||||
subCategoryId: subCategory,
|
||||
});
|
||||
},
|
||||
[selectedCategory, setSelectedSubCategory, setUrlandPushHistory]
|
||||
);
|
||||
|
||||
const filteredPromotedList = useMemo(() => {
|
||||
if (isLoading) return [];
|
||||
const filteredList = searchTerm
|
||||
? list.filter((item) =>
|
||||
(localSearchRef.current!.search(searchTerm) as IntegrationCardItem[])
|
||||
.map((match) => match[searchIdField])
|
||||
.includes(item[searchIdField])
|
||||
)
|
||||
: list;
|
||||
|
||||
return promoteFeaturedIntegrations(filteredList, selectedCategory);
|
||||
}, [isLoading, list, localSearchRef, searchTerm, selectedCategory]);
|
||||
|
||||
const splitSubcategories = (
|
||||
subcategories: CategoryFacet[] | undefined
|
||||
): { visibleSubCategories?: CategoryFacet[]; hiddenSubCategories?: CategoryFacet[] } => {
|
||||
if (!subcategories) return {};
|
||||
else if (subcategories && subcategories?.length < MAX_SUBCATEGORIES_NUMBER) {
|
||||
return { visibleSubCategories: subcategories, hiddenSubCategories: [] };
|
||||
} else if (subcategories && subcategories?.length >= MAX_SUBCATEGORIES_NUMBER) {
|
||||
return {
|
||||
visibleSubCategories: subcategories.slice(0, MAX_SUBCATEGORIES_NUMBER),
|
||||
hiddenSubCategories: subcategories.slice(MAX_SUBCATEGORIES_NUMBER),
|
||||
};
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
const splitSubcat = splitSubcategories(availableSubCategories);
|
||||
const { visibleSubCategories } = splitSubcat;
|
||||
const hiddenSubCategoriesItems = useMemo(() => {
|
||||
return splitSubcat?.hiddenSubCategories?.map((subCategory) => {
|
||||
return (
|
||||
<EuiContextMenuItem
|
||||
key={subCategory.id}
|
||||
onClick={() => {
|
||||
onSubCategoryClick(subCategory.id);
|
||||
closePopover();
|
||||
}}
|
||||
icon={selectedSubCategory === subCategory.id ? 'check' : 'empty'}
|
||||
>
|
||||
{subCategory.title}
|
||||
</EuiContextMenuItem>
|
||||
);
|
||||
});
|
||||
}, [onSubCategoryClick, selectedSubCategory, splitSubcat?.hiddenSubCategories]);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup alignItems="flexStart" gutterSize="xl" data-test-subj="epmList.integrationCards">
|
||||
<EuiFlexItem
|
||||
data-test-subj="epmList.controlsSideColumn"
|
||||
grow={1}
|
||||
style={{
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
zIndex: 100,
|
||||
}}
|
||||
>
|
||||
<ControlsColumn controls={controls} title={title} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={5} data-test-subj="epmList.mainColumn">
|
||||
<SearchBox
|
||||
searchTerm={searchTerm}
|
||||
setSearchTerm={setSearchTerm}
|
||||
selectedCategory={selectedCategory}
|
||||
setCategory={setCategory}
|
||||
categories={categories}
|
||||
availableSubCategories={availableSubCategories}
|
||||
setSelectedSubCategory={setSelectedSubCategory}
|
||||
selectedSubCategory={selectedSubCategory}
|
||||
setUrlandReplaceHistory={setUrlandReplaceHistory}
|
||||
/>
|
||||
{showIntegrationsSubcategories && availableSubCategories?.length ? <EuiSpacer /> : null}
|
||||
{showIntegrationsSubcategories ? (
|
||||
<EuiFlexGroup
|
||||
data-test-subj="epmList.subcategoriesRow"
|
||||
justifyContent="flexStart"
|
||||
direction="row"
|
||||
gutterSize="s"
|
||||
style={{
|
||||
maxWidth: 943,
|
||||
}}
|
||||
>
|
||||
{visibleSubCategories?.map((subCategory) => {
|
||||
const isSelected = subCategory.id === selectedSubCategory;
|
||||
return (
|
||||
<EuiFlexItem grow={false} key={subCategory.id}>
|
||||
<EuiButton
|
||||
css={isSelected ? 'color: white' : ''}
|
||||
color={isSelected ? 'accent' : 'text'}
|
||||
fill={isSelected}
|
||||
aria-label={subCategory?.title}
|
||||
onClick={() => onSubCategoryClick(subCategory.id)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.epmList.subcategoriesButton"
|
||||
defaultMessage="{subcategory}"
|
||||
values={{
|
||||
subcategory: subCategory.title,
|
||||
}}
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
})}
|
||||
{hiddenSubCategoriesItems?.length ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiPopover
|
||||
data-test-subj="epmList.showMoreSubCategoriesButton"
|
||||
id="moreSubCategories"
|
||||
button={
|
||||
<EuiButtonIcon
|
||||
display="base"
|
||||
onClick={onButtonClick}
|
||||
iconType="boxesHorizontal"
|
||||
aria-label="Show more subcategories"
|
||||
size="m"
|
||||
/>
|
||||
}
|
||||
isOpen={isPopoverOpen}
|
||||
closePopover={closePopover}
|
||||
panelPaddingSize="none"
|
||||
anchorPosition="downLeft"
|
||||
>
|
||||
<EuiContextMenuPanel size="s" items={hiddenSubCategoriesItems} />
|
||||
</EuiPopover>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
) : null}
|
||||
{callout ? (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
{callout}
|
||||
</>
|
||||
) : null}
|
||||
<EuiSpacer />
|
||||
<GridColumn
|
||||
isLoading={isLoading || !localSearchRef.current}
|
||||
list={filteredPromotedList}
|
||||
showMissingIntegrationMessage={showMissingIntegrationMessage}
|
||||
showCardLabels={showCardLabels}
|
||||
/>
|
||||
{showMissingIntegrationMessage && (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
<MissingIntegrationContent
|
||||
setUrlandPushHistory={setUrlandPushHistory}
|
||||
resetQuery={resetQuery}
|
||||
setSelectedCategory={setCategory}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { FunctionComponent } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { EuiFieldSearch, EuiText, useEuiTheme, EuiIcon, EuiScreenReaderOnly } from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import type {
|
||||
ExtendedIntegrationCategory,
|
||||
CategoryFacet,
|
||||
} from '../../screens/home/category_facets';
|
||||
|
||||
import type { IntegrationsURLParameters } from '../../screens/home/hooks/use_available_packages';
|
||||
|
||||
export interface Props {
|
||||
searchTerm: string;
|
||||
setSearchTerm: (search: string) => void;
|
||||
selectedCategory: ExtendedIntegrationCategory;
|
||||
setCategory: (category: ExtendedIntegrationCategory) => void;
|
||||
categories: CategoryFacet[];
|
||||
availableSubCategories?: CategoryFacet[];
|
||||
setUrlandReplaceHistory: (params: IntegrationsURLParameters) => void;
|
||||
selectedSubCategory?: string;
|
||||
setSelectedSubCategory?: (c: string | undefined) => void;
|
||||
}
|
||||
|
||||
export const SearchBox: FunctionComponent<Props> = ({
|
||||
searchTerm,
|
||||
setSearchTerm,
|
||||
selectedCategory,
|
||||
setCategory,
|
||||
categories,
|
||||
availableSubCategories,
|
||||
setSelectedSubCategory,
|
||||
selectedSubCategory,
|
||||
setUrlandReplaceHistory,
|
||||
}) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
const onQueryChange = (e: any) => {
|
||||
const queryText = e.target.value;
|
||||
setSearchTerm(queryText);
|
||||
setUrlandReplaceHistory({
|
||||
searchString: queryText,
|
||||
categoryId: selectedCategory,
|
||||
subCategoryId: selectedSubCategory,
|
||||
});
|
||||
};
|
||||
|
||||
const selectedCategoryTitle = selectedCategory
|
||||
? categories.find((category) => category.id === selectedCategory)?.title
|
||||
: undefined;
|
||||
|
||||
const getCategoriesLabel = useMemo(() => {
|
||||
const selectedSubCategoryTitle =
|
||||
selectedSubCategory && availableSubCategories
|
||||
? availableSubCategories.find((subCat) => subCat.id === selectedSubCategory)?.title
|
||||
: undefined;
|
||||
|
||||
if (selectedCategoryTitle && selectedSubCategoryTitle) {
|
||||
return `${selectedCategoryTitle}, ${selectedSubCategoryTitle}`;
|
||||
} else if (selectedCategoryTitle) {
|
||||
return `${selectedCategoryTitle}`;
|
||||
} else return '';
|
||||
}, [availableSubCategories, selectedCategoryTitle, selectedSubCategory]);
|
||||
|
||||
return (
|
||||
<EuiFieldSearch
|
||||
data-test-subj="epmList.searchBar"
|
||||
placeholder={i18n.translate('xpack.fleet.epmList.searchPackagesPlaceholder', {
|
||||
defaultMessage: 'Search for integrations',
|
||||
})}
|
||||
value={searchTerm}
|
||||
onChange={(e) => onQueryChange(e)}
|
||||
isClearable={true}
|
||||
incremental={true}
|
||||
fullWidth={true}
|
||||
prepend={
|
||||
selectedCategoryTitle ? (
|
||||
<EuiText
|
||||
data-test-subj="epmList.categoryBadge"
|
||||
size="xs"
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
fontWeight: euiTheme.font.weight.bold,
|
||||
backgroundColor: euiTheme.colors.lightestShade,
|
||||
}}
|
||||
>
|
||||
<EuiScreenReaderOnly>
|
||||
<span>Searching category: </span>
|
||||
</EuiScreenReaderOnly>
|
||||
{getCategoriesLabel}
|
||||
<button
|
||||
data-test-subj="epmList.categoryBadge.closeBtn"
|
||||
onClick={() => {
|
||||
setCategory('');
|
||||
if (setSelectedSubCategory) setSelectedSubCategory(undefined);
|
||||
setUrlandReplaceHistory({
|
||||
searchString: '',
|
||||
categoryId: '',
|
||||
subCategoryId: '',
|
||||
});
|
||||
}}
|
||||
aria-label="Remove filter"
|
||||
style={{
|
||||
padding: euiTheme.size.xs,
|
||||
paddingTop: '2px',
|
||||
}}
|
||||
>
|
||||
<EuiIcon
|
||||
type="cross"
|
||||
color="text"
|
||||
size="s"
|
||||
style={{
|
||||
width: 'auto',
|
||||
padding: 0,
|
||||
backgroundColor: euiTheme.colors.lightestShade,
|
||||
}}
|
||||
/>
|
||||
</button>
|
||||
</EuiText>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -5,23 +5,20 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { useLocation, useParams, useHistory } from 'react-router-dom';
|
||||
|
||||
import { uniq, xorBy } from 'lodash';
|
||||
|
||||
import type { CustomIntegration } from '@kbn/custom-integrations-plugin/common';
|
||||
|
||||
import type { IntegrationPreferenceType } from '../../../components/integration_preference';
|
||||
import { usePackages, useCategories, useStartServices } from '../../../../../hooks';
|
||||
import { usePackages, useCategories } from '../../../../../hooks';
|
||||
import {
|
||||
useGetAppendCustomIntegrations,
|
||||
useGetReplacementCustomIntegrations,
|
||||
useLink,
|
||||
} from '../../../../../hooks';
|
||||
import { useMergeEprPackagesWithReplacements } from '../../../../../hooks/use_merge_epr_with_replacements';
|
||||
|
||||
import type { CategoryParams } from '..';
|
||||
import { getParams, mapToCard } from '..';
|
||||
import { mapToCard } from '..';
|
||||
import type { PackageList, PackageListItem } from '../../../../../types';
|
||||
|
||||
import { doesPackageHaveIntegrations } from '../../../../../services';
|
||||
|
@ -31,8 +28,6 @@ import {
|
|||
isIntegrationPolicyTemplate,
|
||||
} from '../../../../../../../../common/services';
|
||||
|
||||
import { pagePathGetters } from '../../../../../constants';
|
||||
|
||||
import type { IntegrationCardItem } from '../../../../../../../../common/types/models';
|
||||
|
||||
import { ALL_CATEGORY } from '../category_facets';
|
||||
|
@ -40,6 +35,8 @@ import type { CategoryFacet } from '../category_facets';
|
|||
|
||||
import { mergeCategoriesAndCount } from '../util';
|
||||
|
||||
import { useBuildIntegrationsUrl } from './use_build_integrations_url';
|
||||
|
||||
export interface IntegrationsURLParameters {
|
||||
searchString?: string;
|
||||
categoryId?: string;
|
||||
|
@ -111,14 +108,17 @@ export const useAvailablePackages = () => {
|
|||
const [prereleaseIntegrationsEnabled, setPrereleaseIntegrationsEnabled] = React.useState<
|
||||
boolean | undefined
|
||||
>(undefined);
|
||||
const { http } = useStartServices();
|
||||
const addBasePath = http.basePath.prepend;
|
||||
|
||||
const {
|
||||
selectedCategory: initialSelectedCategory,
|
||||
selectedSubcategory: initialSubcategory,
|
||||
initialSelectedCategory,
|
||||
initialSubcategory,
|
||||
setUrlandPushHistory,
|
||||
setUrlandReplaceHistory,
|
||||
getHref,
|
||||
getAbsolutePath,
|
||||
searchParam,
|
||||
} = getParams(useParams<CategoryParams>(), useLocation().search);
|
||||
addBasePath,
|
||||
} = useBuildIntegrationsUrl();
|
||||
|
||||
const [selectedCategory, setCategory] = useState(initialSelectedCategory);
|
||||
const [selectedSubCategory, setSelectedSubCategory] = useState<string | undefined>(
|
||||
|
@ -126,45 +126,6 @@ export const useAvailablePackages = () => {
|
|||
);
|
||||
const [searchTerm, setSearchTerm] = useState(searchParam || '');
|
||||
|
||||
const { getHref, getAbsolutePath } = useLink();
|
||||
const history = useHistory();
|
||||
|
||||
const buildUrl = ({ searchString, categoryId, subCategoryId }: IntegrationsURLParameters) => {
|
||||
const url = pagePathGetters.integrations_all({
|
||||
category: categoryId ? categoryId : '',
|
||||
subCategory: subCategoryId ? subCategoryId : '',
|
||||
searchTerm: searchString ? searchString : '',
|
||||
})[1];
|
||||
return url;
|
||||
};
|
||||
|
||||
const setUrlandPushHistory = ({
|
||||
searchString,
|
||||
categoryId,
|
||||
subCategoryId,
|
||||
}: IntegrationsURLParameters) => {
|
||||
const url = buildUrl({
|
||||
categoryId,
|
||||
searchString,
|
||||
subCategoryId,
|
||||
});
|
||||
history.push(url);
|
||||
};
|
||||
|
||||
const setUrlandReplaceHistory = ({
|
||||
searchString,
|
||||
categoryId,
|
||||
subCategoryId,
|
||||
}: IntegrationsURLParameters) => {
|
||||
const url = buildUrl({
|
||||
categoryId,
|
||||
searchString,
|
||||
subCategoryId,
|
||||
});
|
||||
// Use .replace so the browser's back button is not tied to single keystroke
|
||||
history.replace(url);
|
||||
};
|
||||
|
||||
const {
|
||||
data: eprPackages,
|
||||
isLoading: isLoadingAllPackages,
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { useLocation, useParams, useHistory } from 'react-router-dom';
|
||||
|
||||
import { useStartServices } from '../../../../../hooks';
|
||||
import { useLink } from '../../../../../hooks';
|
||||
|
||||
import type { CategoryParams } from '..';
|
||||
import { getParams } from '..';
|
||||
|
||||
import { pagePathGetters } from '../../../../../constants';
|
||||
|
||||
export interface IntegrationsURLParameters {
|
||||
searchString?: string;
|
||||
categoryId?: string;
|
||||
subCategoryId?: string;
|
||||
}
|
||||
|
||||
export const useBuildIntegrationsUrl = () => {
|
||||
const { http } = useStartServices();
|
||||
const addBasePath = http.basePath.prepend;
|
||||
|
||||
const {
|
||||
selectedCategory: initialSelectedCategory,
|
||||
selectedSubcategory: initialSubcategory,
|
||||
searchParam,
|
||||
} = getParams(useParams<CategoryParams>(), useLocation().search);
|
||||
|
||||
const { getHref, getAbsolutePath } = useLink();
|
||||
const history = useHistory();
|
||||
|
||||
const buildUrl = ({ searchString, categoryId, subCategoryId }: IntegrationsURLParameters) => {
|
||||
const url = pagePathGetters.integrations_all({
|
||||
category: categoryId ? categoryId : '',
|
||||
subCategory: subCategoryId ? subCategoryId : '',
|
||||
searchTerm: searchString ? searchString : '',
|
||||
})[1];
|
||||
return url;
|
||||
};
|
||||
|
||||
const setUrlandPushHistory = ({
|
||||
searchString,
|
||||
categoryId,
|
||||
subCategoryId,
|
||||
}: IntegrationsURLParameters) => {
|
||||
const url = buildUrl({
|
||||
categoryId,
|
||||
searchString,
|
||||
subCategoryId,
|
||||
});
|
||||
history.push(url);
|
||||
};
|
||||
|
||||
const setUrlandReplaceHistory = ({
|
||||
searchString,
|
||||
categoryId,
|
||||
subCategoryId,
|
||||
}: IntegrationsURLParameters) => {
|
||||
const url = buildUrl({
|
||||
categoryId,
|
||||
searchString,
|
||||
subCategoryId,
|
||||
});
|
||||
// Use .replace so the browser's back button is not tied to single keystroke
|
||||
history.replace(url);
|
||||
};
|
||||
|
||||
return {
|
||||
initialSelectedCategory,
|
||||
initialSubcategory,
|
||||
setUrlandPushHistory,
|
||||
setUrlandReplaceHistory,
|
||||
getHref,
|
||||
getAbsolutePath,
|
||||
searchParam,
|
||||
addBasePath,
|
||||
};
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue