[Enterprise Search] [Behavioral analytics] Update collection navigation (#154091)

-  Implement Breadcrumb on the top of the page that is represented as
Enterprise Search -> Behavioral Analytics -> %collection_name% (if
present)
-  Implement Navigation on the side menu
<img width="1129" alt="image"
src="https://user-images.githubusercontent.com/17390745/228927169-b692a4f8-95ca-4b1f-8c52-78f909cb51a1.png">
This commit is contained in:
Yan Savitski 2023-03-31 12:39:18 +02:00 committed by GitHub
parent 0ea5cdc52b
commit 1f2f3a801f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 287 additions and 28 deletions

View file

@ -24,12 +24,6 @@ import { AnalyticsCollectionToolbarLogic } from './analytics_collection_toolbar/
import { FetchAnalyticsCollectionLogic } from './fetch_analytics_collection_logic';
export const collectionViewBreadcrumbs = [
i18n.translate('xpack.enterpriseSearch.analytics.collectionsView.breadcrumb', {
defaultMessage: 'View collection',
}),
];
export const AnalyticsCollectionView: React.FC = () => {
const { fetchAnalyticsCollection } = useActions(FetchAnalyticsCollectionLogic);
const { setTimeRange } = useActions(AnalyticsCollectionToolbarLogic);
@ -45,7 +39,8 @@ export const AnalyticsCollectionView: React.FC = () => {
<EnterpriseSearchAnalyticsPageTemplate
restrictWidth
isLoading={isLoading}
pageChrome={[...collectionViewBreadcrumbs]}
pageChrome={[analyticsCollection?.name]}
analyticsName={analyticsCollection?.name}
pageViewTelemetry={`View Analytics Collection - ${section}`}
pageHeader={{
bottomBorder: false,

View file

@ -8,13 +8,14 @@
import React from 'react';
jest.mock('../../../shared/layout/nav', () => ({
useEnterpriseSearchNav: () => [],
useEnterpriseSearchAnalyticsNav: jest.fn().mockReturnValue([]),
}));
import { shallow } from 'enzyme';
import { SetAnalyticsChrome } from '../../../shared/kibana_chrome';
import { EnterpriseSearchPageTemplateWrapper } from '../../../shared/layout';
import { useEnterpriseSearchAnalyticsNav } from '../../../shared/layout/nav';
import { SendEnterpriseSearchTelemetry } from '../../../shared/telemetry';
import { EnterpriseSearchAnalyticsPageTemplate } from './page_template';
@ -71,5 +72,23 @@ describe('EnterpriseSearchAnalyticsPageTemplate', () => {
expect(wrapper.find(EnterpriseSearchPageTemplateWrapper).prop('isLoading')).toEqual(false);
expect(wrapper.find(EnterpriseSearchPageTemplateWrapper).prop('emptyState')).toEqual(<div />);
});
it('passes down analytics name and paths to useEnterpriseSearchAnalyticsNav', () => {
const mockAnalyticsName = 'some_analytics_name';
shallow(
<EnterpriseSearchAnalyticsPageTemplate
analyticsName={mockAnalyticsName}
pageHeader={{ pageTitle: 'hello world' }}
isLoading={false}
emptyState={<div />}
/>
);
expect(useEnterpriseSearchAnalyticsNav).toHaveBeenCalledWith(mockAnalyticsName, {
explorer: '/collections/some_analytics_name/explorer',
integration: '/collections/some_analytics_name/integrate',
overview: '/collections/some_analytics_name/overview',
});
});
});
});

View file

@ -8,25 +8,45 @@
import React from 'react';
import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../common/constants';
import { SetAnalyticsChrome } from '../../../shared/kibana_chrome';
import {
EnterpriseSearchPageTemplateWrapper,
PageTemplateProps,
useEnterpriseSearchNav,
} from '../../../shared/layout';
import { SendEnterpriseSearchTelemetry } from '../../../shared/telemetry';
import { generateEncodedPath } from '../../../shared/encode_path_params';
export const EnterpriseSearchAnalyticsPageTemplate: React.FC<PageTemplateProps> = ({
children,
pageChrome,
pageViewTelemetry,
...pageTemplateProps
}) => {
import { SetAnalyticsChrome } from '../../../shared/kibana_chrome';
import { EnterpriseSearchPageTemplateWrapper, PageTemplateProps } from '../../../shared/layout';
import { useEnterpriseSearchAnalyticsNav } from '../../../shared/layout/nav';
import { SendEnterpriseSearchTelemetry } from '../../../shared/telemetry';
import {
COLLECTION_EXPLORER_PATH,
COLLECTION_INTEGRATE_PATH,
COLLECTION_VIEW_PATH,
} from '../../routes';
interface EnterpriseSearchAnalyticsPageTemplateProps extends PageTemplateProps {
analyticsName?: string;
}
export const EnterpriseSearchAnalyticsPageTemplate: React.FC<
EnterpriseSearchAnalyticsPageTemplateProps
> = ({ children, analyticsName, pageChrome, pageViewTelemetry, ...pageTemplateProps }) => {
return (
<EnterpriseSearchPageTemplateWrapper
{...pageTemplateProps}
solutionNav={{
items: useEnterpriseSearchNav(),
items: useEnterpriseSearchAnalyticsNav(
analyticsName,
analyticsName
? {
explorer: generateEncodedPath(COLLECTION_EXPLORER_PATH, {
name: analyticsName,
}),
integration: generateEncodedPath(COLLECTION_INTEGRATE_PATH, {
name: analyticsName,
}),
overview: generateEncodedPath(COLLECTION_VIEW_PATH, {
name: analyticsName,
}),
}
: undefined
),
name: ENTERPRISE_SEARCH_CONTENT_PLUGIN.NAME,
}}
setPageChrome={pageChrome && <SetAnalyticsChrome trail={pageChrome} />}

View file

@ -17,7 +17,12 @@ import { VersionMismatchPage } from '../shared/version_mismatch';
import { AnalyticsCollectionView } from './components/analytics_collection_view/analytics_collection_view';
import { AnalyticsOverview } from './components/analytics_overview/analytics_overview';
import { ROOT_PATH, COLLECTION_VIEW_PATH, COLLECTION_INTEGRATE_PATH } from './routes';
import {
ROOT_PATH,
COLLECTION_VIEW_PATH,
COLLECTION_INTEGRATE_PATH,
COLLECTION_EXPLORER_PATH,
} from './routes';
export const Analytics: React.FC<InitialAppData> = (props) => {
const { enterpriseSearchVersion, kibanaVersion } = props;
@ -40,6 +45,8 @@ export const Analytics: React.FC<InitialAppData> = (props) => {
</Route>
<Route exact path={COLLECTION_INTEGRATE_PATH} />
<Route exact path={COLLECTION_EXPLORER_PATH} />
</Switch>
);
};

View file

@ -9,3 +9,4 @@ export const ROOT_PATH = '/';
export const COLLECTIONS_PATH = '/collections';
export const COLLECTION_VIEW_PATH = `${COLLECTIONS_PATH}/:name/overview`;
export const COLLECTION_INTEGRATE_PATH = `${COLLECTIONS_PATH}/:name/integrate`;
export const COLLECTION_EXPLORER_PATH = `${COLLECTIONS_PATH}/:name/explorer`;

View file

@ -16,7 +16,11 @@ import { EuiSideNavItemType } from '@elastic/eui';
import { DEFAULT_PRODUCT_FEATURES } from '../../../../common/constants';
import { ProductAccess } from '../../../../common/types';
import { useEnterpriseSearchNav, useEnterpriseSearchEngineNav } from './nav';
import {
useEnterpriseSearchNav,
useEnterpriseSearchEngineNav,
useEnterpriseSearchAnalyticsNav,
} from './nav';
const DEFAULT_PRODUCT_ACCESS: ProductAccess = {
hasAppSearchAccess: true,
@ -532,3 +536,162 @@ describe('useEnterpriseSearchEngineNav', () => {
});
});
});
describe('useEnterpriseSearchAnalyticsNav', () => {
const baseNavs = [
{
href: '/app/enterprise_search/overview',
id: 'es_overview',
name: 'Overview',
},
{
id: 'content',
items: [
{
href: '/app/enterprise_search/content/search_indices',
id: 'search_indices',
name: 'Indices',
},
],
name: 'Content',
},
{
id: 'enterpriseSearchAnalytics',
items: [
{
href: '/app/enterprise_search/analytics',
id: 'analytics_collections',
name: 'Collections',
},
],
name: 'Behavioral Analytics',
},
{
id: 'search',
items: [
{
href: '/app/enterprise_search/elasticsearch',
id: 'elasticsearch',
name: 'Elasticsearch',
},
{
href: '/app/enterprise_search/search_experiences',
id: 'searchExperiences',
name: 'Search Experiences',
},
{
href: '/app/enterprise_search/app_search',
id: 'app_search',
name: 'App Search',
},
{
href: '/app/enterprise_search/workplace_search',
id: 'workplace_search',
name: 'Workplace Search',
},
],
name: 'Search',
},
];
beforeEach(() => {
jest.clearAllMocks();
setMockValues({});
});
it('returns basic nav all params are empty', () => {
const navItems = useEnterpriseSearchAnalyticsNav();
expect(navItems).toEqual(baseNavs);
});
it('returns basic nav if only name provided', () => {
const navItems = useEnterpriseSearchAnalyticsNav('my-test-collection');
expect(navItems).toEqual(baseNavs);
});
it('returns nav with sub items when name and paths provided', () => {
const navItems = useEnterpriseSearchAnalyticsNav('my-test-collection', {
explorer: '/explorer-path',
integration: '/integration-path',
overview: '/overview-path',
});
expect(navItems).toEqual([
{
href: '/app/enterprise_search/overview',
id: 'es_overview',
name: 'Overview',
},
{
id: 'content',
items: [
{
href: '/app/enterprise_search/content/search_indices',
id: 'search_indices',
name: 'Indices',
},
],
name: 'Content',
},
{
id: 'enterpriseSearchAnalytics',
items: [
{
href: '/app/enterprise_search/analytics',
id: 'analytics_collections',
items: [
{
id: 'analytics_collections',
items: [
{
href: '/app/enterprise_search/analytics/overview-path',
id: 'enterpriseSearchEngineOverview',
name: 'Overview',
},
{
href: '/app/enterprise_search/analytics/explorer-path',
id: 'enterpriseSearchEngineIndices',
name: 'Explorer',
},
{
href: '/app/enterprise_search/analytics/integration-path',
id: 'enterpriseSearchEngineSchema',
name: 'Integration',
},
],
name: 'my-test-collection',
},
],
name: 'Collections',
},
],
name: 'Behavioral Analytics',
},
{
id: 'search',
items: [
{
href: '/app/enterprise_search/elasticsearch',
id: 'elasticsearch',
name: 'Elasticsearch',
},
{
href: '/app/enterprise_search/search_experiences',
id: 'searchExperiences',
name: 'Search Experiences',
},
{
href: '/app/enterprise_search/app_search',
id: 'app_search',
name: 'App Search',
},
{
href: '/app/enterprise_search/workplace_search',
id: 'workplace_search',
name: 'Workplace Search',
},
],
name: 'Search',
},
]);
});
});

View file

@ -89,7 +89,6 @@ export const useEnterpriseSearchNav = () => {
}),
...generateNavLink({
shouldNotCreateHref: true,
shouldShowActiveForSubroutes: true,
to: ANALYTICS_PLUGIN.URL,
}),
},
@ -332,3 +331,61 @@ export const useEnterpriseSearchEngineNav = (engineName?: string, isEmptyState?:
return navItems;
};
export const useEnterpriseSearchAnalyticsNav = (
name?: string,
paths?: {
explorer: string;
integration: string;
overview: string;
}
) => {
const navItems = useEnterpriseSearchNav();
const collectionNav = navItems.find(
(item) =>
item.id === 'enterpriseSearchAnalytics' && item.items?.[0]?.id === 'analytics_collections'
)?.items?.[0];
if (!name || !paths || !collectionNav) return navItems;
collectionNav.items = [
{
id: 'analytics_collections',
items: [
{
id: 'enterpriseSearchEngineOverview',
name: i18n.translate('xpack.enterpriseSearch.nav.analyticsCollections.overviewTitle', {
defaultMessage: 'Overview',
}),
...generateNavLink({
shouldNotCreateHref: true,
to: ANALYTICS_PLUGIN.URL + paths.overview,
}),
},
{
id: 'enterpriseSearchEngineIndices',
name: i18n.translate('xpack.enterpriseSearch.nav.analyticsCollections.explorerTitle', {
defaultMessage: 'Explorer',
}),
...generateNavLink({
shouldNotCreateHref: true,
to: ANALYTICS_PLUGIN.URL + paths.explorer,
}),
},
{
id: 'enterpriseSearchEngineSchema',
name: i18n.translate('xpack.enterpriseSearch.nav.analyticsCollections.integrationTitle', {
defaultMessage: 'Integration',
}),
...generateNavLink({
shouldNotCreateHref: true,
to: ANALYTICS_PLUGIN.URL + paths.integration,
}),
},
],
name,
},
];
return navItems;
};

View file

@ -11437,7 +11437,6 @@
"xpack.enterpriseSearch.analytics.collectionsCreate.form.subtitle": "Une collection d'analyse permet de stocker les événements d'analyse pour toute application de recherche que vous créez. Affectez-lui un nom facile à retenir ci-dessous.",
"xpack.enterpriseSearch.analytics.collectionsCreate.form.title": "Créer une collection d'analyses",
"xpack.enterpriseSearch.analytics.collectionsDelete.action.successMessage": "La collection a été supprimée avec succès",
"xpack.enterpriseSearch.analytics.collectionsView.breadcrumb": "Afficher la collection",
"xpack.enterpriseSearch.analytics.productCardDescription": "Tableaux de bord et outils permettant de visualiser le comportement des utilisateurs finaux et de mesurer les performances de vos applications de recherche.",
"xpack.enterpriseSearch.analytics.productDescription": "Tableaux de bord et outils permettant de visualiser le comportement des utilisateurs finaux et de mesurer les performances de vos applications de recherche.",
"xpack.enterpriseSearch.analytics.productName": "Behavioral Analytics",

View file

@ -11436,7 +11436,6 @@
"xpack.enterpriseSearch.analytics.collectionsCreate.form.subtitle": "分析コレクションには、構築している特定の検索アプリケーションの分析イベントを格納できます。以下で覚えやすい名前を指定してください。",
"xpack.enterpriseSearch.analytics.collectionsCreate.form.title": "分析コレクションを作成",
"xpack.enterpriseSearch.analytics.collectionsDelete.action.successMessage": "コレクションが正常に削除されました",
"xpack.enterpriseSearch.analytics.collectionsView.breadcrumb": "コレクションを表示",
"xpack.enterpriseSearch.analytics.productCardDescription": "エンドユーザーの行動を可視化し、検索アプリケーションのパフォーマンスを測定するためのダッシュボードとツール。",
"xpack.enterpriseSearch.analytics.productDescription": "エンドユーザーの行動を可視化し、検索アプリケーションのパフォーマンスを測定するためのダッシュボードとツール。",
"xpack.enterpriseSearch.analytics.productName": "Behavioral Analytics",

View file

@ -11437,7 +11437,6 @@
"xpack.enterpriseSearch.analytics.collectionsCreate.form.subtitle": "分析集合为您正在构建的任何给定搜索应用程序提供了一个用于存储分析事件的位置。在下面为其提供好记的名称。",
"xpack.enterpriseSearch.analytics.collectionsCreate.form.title": "创建分析集合",
"xpack.enterpriseSearch.analytics.collectionsDelete.action.successMessage": "已成功删除此集合",
"xpack.enterpriseSearch.analytics.collectionsView.breadcrumb": "查看集合",
"xpack.enterpriseSearch.analytics.productCardDescription": "用于对最终用户行为进行可视化并评估搜索应用程序性能的仪表板和工具。",
"xpack.enterpriseSearch.analytics.productDescription": "用于对最终用户行为进行可视化并评估搜索应用程序性能的仪表板和工具。",
"xpack.enterpriseSearch.analytics.productName": "行为分析",