From c1e21deac6c936ef92cc0b23c0a66139b9697897 Mon Sep 17 00:00:00 2001 From: Constance Date: Wed, 13 Jan 2021 11:08:20 -0800 Subject: [PATCH] [App Search] Set up Analytics router (#88095) * [Setup] Analytics routes & page title consts * Add AnalyticsRouter - with TODO views * Update EngineRouter to use AnalyticsRouter + minor rearranging of import order + update EngineNav to show active flag for subroutes * [Polish] Add 404 fallback to Analytics subroutes + add custom breadcrumb trail prop to NotFound component * [PR feedback] DRY out typing --- .../analytics/analytics_router.test.tsx | 21 ++++++ .../components/analytics/analytics_router.tsx | 72 +++++++++++++++++++ .../components/analytics/constants.ts | 26 ++++++- .../app_search/components/analytics/index.ts | 1 + .../components/engine/engine_nav.tsx | 6 +- .../components/engine/engine_router.test.tsx | 3 +- .../components/engine/engine_router.tsx | 7 +- .../public/applications/app_search/routes.ts | 7 +- .../shared/not_found/not_found.test.tsx | 9 +++ .../shared/not_found/not_found.tsx | 7 +- 10 files changed, 147 insertions(+), 12 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.test.tsx new file mode 100644 index 000000000000..6b04a668b148 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.test.tsx @@ -0,0 +1,21 @@ +/* + * 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 React from 'react'; +import { shallow } from 'enzyme'; +import { Route, Switch } from 'react-router-dom'; + +import { AnalyticsRouter } from './'; + +describe('AnalyticsRouter', () => { + // Detailed route testing is better done via E2E tests + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(Switch)).toHaveLength(1); + expect(wrapper.find(Route)).toHaveLength(8); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.tsx new file mode 100644 index 000000000000..117d14f7ca83 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.tsx @@ -0,0 +1,72 @@ +/* + * 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 React from 'react'; +import { Route, Switch } from 'react-router-dom'; + +import { APP_SEARCH_PLUGIN } from '../../../../../common/constants'; +import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; +import { NotFound } from '../../../shared/not_found'; +import { + ENGINE_PATH, + ENGINE_ANALYTICS_PATH, + ENGINE_ANALYTICS_TOP_QUERIES_PATH, + ENGINE_ANALYTICS_TOP_QUERIES_NO_RESULTS_PATH, + ENGINE_ANALYTICS_TOP_QUERIES_NO_CLICKS_PATH, + ENGINE_ANALYTICS_TOP_QUERIES_WITH_CLICKS_PATH, + ENGINE_ANALYTICS_RECENT_QUERIES_PATH, + ENGINE_ANALYTICS_QUERY_DETAIL_PATH, +} from '../../routes'; +import { + ANALYTICS_TITLE, + TOP_QUERIES, + TOP_QUERIES_NO_RESULTS, + TOP_QUERIES_NO_CLICKS, + TOP_QUERIES_WITH_CLICKS, + RECENT_QUERIES, +} from './constants'; + +interface Props { + engineBreadcrumb: string[]; +} +export const AnalyticsRouter: React.FC = ({ engineBreadcrumb }) => { + const ANALYTICS_BREADCRUMB = [...engineBreadcrumb, ANALYTICS_TITLE]; + + return ( + + + + TODO: Analytics overview + + + + TODO: Top queries + + + + TODO: Top queries with no results + + + + TODO: Top queries with no clicks + + + + TODO: Top queries with clicks + + + + TODO: Recent queries + + + TODO: Query detail page + + + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts index 9985753d0970..c1087bf4cb2f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts @@ -11,26 +11,46 @@ export const ANALYTICS_TITLE = i18n.translate( { defaultMessage: 'Analytics' } ); +// Total card titles export const TOTAL_DOCUMENTS = i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.analytics.totalDocuments', { defaultMessage: 'Total documents' } ); - export const TOTAL_API_OPERATIONS = i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.analytics.totalApiOperations', { defaultMessage: 'Total API operations' } ); - export const TOTAL_QUERIES = i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.analytics.totalQueries', { defaultMessage: 'Total queries' } ); - export const TOTAL_CLICKS = i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.analytics.totalClicks', { defaultMessage: 'Total clicks' } ); +// Queries sub-pages +export const TOP_QUERIES = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.analytics.topQueriesTitle', + { defaultMessage: 'Top queries' } +); +export const TOP_QUERIES_NO_RESULTS = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.analytics.topQueriesNoResultsTitle', + { defaultMessage: 'Top queries with no results' } +); +export const TOP_QUERIES_NO_CLICKS = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.analytics.topQueriesNoClicksTitle', + { defaultMessage: 'Top queries with no clicks' } +); +export const TOP_QUERIES_WITH_CLICKS = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.analytics.topQueriesWithClicksTitle', + { defaultMessage: 'Top queries with clicks' } +); +export const RECENT_QUERIES = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.analytics.recentQueriesTitle', + { defaultMessage: 'Recent queries' } +); + // Moment date format conversions export const SERVER_DATE_FORMAT = 'YYYY-MM-DD'; export const TOOLTIP_DATE_FORMAT = 'MMMM D, YYYY'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/index.ts index 3b201b38703d..0ab5ab80e835 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/index.ts @@ -5,5 +5,6 @@ */ export { ANALYTICS_TITLE } from './constants'; +export { AnalyticsRouter } from './analytics_router'; export { AnalyticsChart } from './components'; export { convertToChartData } from './utils'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx index 418ab33457d0..40ae2cef0acb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx @@ -103,7 +103,11 @@ export const EngineNav: React.FC = () => { {OVERVIEW_TITLE} {canViewEngineAnalytics && ( - + {ANALYTICS_TITLE} )} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx index cbaa347d6573..26c7b3f677fc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx @@ -19,6 +19,7 @@ import { setQueuedErrorMessage } from '../../../shared/flash_messages'; import { Loading } from '../../../shared/loading'; import { EngineOverview } from '../engine_overview'; +import { AnalyticsRouter } from '../analytics'; import { EngineRouter } from './'; @@ -93,6 +94,6 @@ describe('EngineRouter', () => { setMockValues({ ...values, myRole: { canViewEngineAnalytics: true } }); const wrapper = shallow(); - expect(wrapper.find('[data-test-subj="AnalyticsTODO"]')).toHaveLength(1); + expect(wrapper.find(AnalyticsRouter)).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx index 1d2f3f640f34..47fe302ac701 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx @@ -33,13 +33,13 @@ import { } from '../../routes'; import { ENGINES_TITLE } from '../engines'; import { OVERVIEW_TITLE } from '../engine_overview'; -import { ANALYTICS_TITLE } from '../analytics'; import { Loading } from '../../../shared/loading'; import { EngineOverview } from '../engine_overview'; +import { AnalyticsRouter } from '../analytics'; +import { DocumentDetail, Documents } from '../documents'; import { EngineLogic } from './'; -import { DocumentDetail, Documents } from '../documents'; export const EngineRouter: React.FC = () => { const { @@ -87,8 +87,7 @@ export const EngineRouter: React.FC = () => { {canViewEngineAnalytics && ( - -
Just testing right now
+
)} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts index ca2ce177617b..e8c6948438fe 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts @@ -25,7 +25,12 @@ export const SAMPLE_ENGINE_PATH = '/engines/national-parks-demo'; export const getEngineRoute = (engineName: string) => generatePath(ENGINE_PATH, { engineName }); export const ENGINE_ANALYTICS_PATH = '/analytics'; -// TODO: Analytics sub-pages +export const ENGINE_ANALYTICS_TOP_QUERIES_PATH = `${ENGINE_ANALYTICS_PATH}/top_queries`; +export const ENGINE_ANALYTICS_TOP_QUERIES_NO_CLICKS_PATH = `${ENGINE_ANALYTICS_PATH}/top_queries_no_clicks`; +export const ENGINE_ANALYTICS_TOP_QUERIES_NO_RESULTS_PATH = `${ENGINE_ANALYTICS_PATH}/top_queries_no_results`; +export const ENGINE_ANALYTICS_TOP_QUERIES_WITH_CLICKS_PATH = `${ENGINE_ANALYTICS_PATH}/top_queries_with_clicks`; +export const ENGINE_ANALYTICS_RECENT_QUERIES_PATH = `${ENGINE_ANALYTICS_PATH}/recent_queries`; +export const ENGINE_ANALYTICS_QUERY_DETAIL_PATH = `${ENGINE_ANALYTICS_PATH}/query_detail/:query`; export const ENGINE_DOCUMENTS_PATH = '/documents'; export const ENGINE_DOCUMENT_DETAIL_PATH = `${ENGINE_DOCUMENTS_PATH}/:documentId`; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.test.tsx index 62c0af31cffd..083173c8e7a4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.test.tsx @@ -13,6 +13,7 @@ import { shallow } from 'enzyme'; import { EuiButton as EuiButtonExternal, EuiEmptyPrompt } from '@elastic/eui'; import { APP_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN } from '../../../../common/constants'; +import { SetAppSearchChrome } from '../kibana_chrome'; import { AppSearchLogo } from './assets/app_search_logo'; import { WorkplaceSearchLogo } from './assets/workplace_search_logo'; @@ -51,6 +52,14 @@ describe('NotFound', () => { expect(prompt.find(EuiButtonExternal).prop('href')).toEqual('https://support.elastic.co'); }); + it('passes down optional custom breadcrumbs', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(SetAppSearchChrome).prop('trail')).toEqual(['Hello', 'World']); + }); + it('does not render anything without a valid product', () => { const wrapper = shallow(); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.tsx index d0140b873022..5c76b5dae1e4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.tsx @@ -23,6 +23,7 @@ import { } from '../../../../common/constants'; import { EuiButtonTo } from '../react_router_helpers'; +import { BreadcrumbTrail } from '../kibana_chrome/generate_breadcrumbs'; import { SetAppSearchChrome, SetWorkplaceSearchChrome } from '../kibana_chrome'; import { SendAppSearchTelemetry, SendWorkplaceSearchTelemetry } from '../telemetry'; import { LicensingLogic } from '../licensing'; @@ -37,9 +38,11 @@ interface NotFoundProps { ID: string; SUPPORT_URL: string; }; + // Optional breadcrumbs + breadcrumbs?: BreadcrumbTrail; } -export const NotFound: React.FC = ({ product = {} }) => { +export const NotFound: React.FC = ({ product = {}, breadcrumbs }) => { const { hasGoldLicense } = useValues(LicensingLogic); const supportUrl = hasGoldLicense ? LICENSED_SUPPORT_URL : product.SUPPORT_URL; @@ -64,7 +67,7 @@ export const NotFound: React.FC = ({ product = {} }) => { return ( <> - +