mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[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
This commit is contained in:
parent
e07c541036
commit
c1e21deac6
10 changed files with 147 additions and 12 deletions
|
@ -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(<AnalyticsRouter engineBreadcrumb={['Engines', 'some-engine']} />);
|
||||
|
||||
expect(wrapper.find(Switch)).toHaveLength(1);
|
||||
expect(wrapper.find(Route)).toHaveLength(8);
|
||||
});
|
||||
});
|
|
@ -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<Props> = ({ engineBreadcrumb }) => {
|
||||
const ANALYTICS_BREADCRUMB = [...engineBreadcrumb, ANALYTICS_TITLE];
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route exact path={ENGINE_PATH + ENGINE_ANALYTICS_PATH}>
|
||||
<SetPageChrome trail={ANALYTICS_BREADCRUMB} />
|
||||
TODO: Analytics overview
|
||||
</Route>
|
||||
<Route exact path={ENGINE_PATH + ENGINE_ANALYTICS_TOP_QUERIES_PATH}>
|
||||
<SetPageChrome trail={[...ANALYTICS_BREADCRUMB, TOP_QUERIES]} />
|
||||
TODO: Top queries
|
||||
</Route>
|
||||
<Route exact path={ENGINE_PATH + ENGINE_ANALYTICS_TOP_QUERIES_NO_RESULTS_PATH}>
|
||||
<SetPageChrome trail={[...ANALYTICS_BREADCRUMB, TOP_QUERIES_NO_RESULTS]} />
|
||||
TODO: Top queries with no results
|
||||
</Route>
|
||||
<Route exact path={ENGINE_PATH + ENGINE_ANALYTICS_TOP_QUERIES_NO_CLICKS_PATH}>
|
||||
<SetPageChrome trail={[...ANALYTICS_BREADCRUMB, TOP_QUERIES_NO_CLICKS]} />
|
||||
TODO: Top queries with no clicks
|
||||
</Route>
|
||||
<Route exact path={ENGINE_PATH + ENGINE_ANALYTICS_TOP_QUERIES_WITH_CLICKS_PATH}>
|
||||
<SetPageChrome trail={[...ANALYTICS_BREADCRUMB, TOP_QUERIES_WITH_CLICKS]} />
|
||||
TODO: Top queries with clicks
|
||||
</Route>
|
||||
<Route exact path={ENGINE_PATH + ENGINE_ANALYTICS_RECENT_QUERIES_PATH}>
|
||||
<SetPageChrome trail={[...ANALYTICS_BREADCRUMB, RECENT_QUERIES]} />
|
||||
TODO: Recent queries
|
||||
</Route>
|
||||
<Route exact path={ENGINE_PATH + ENGINE_ANALYTICS_QUERY_DETAIL_PATH}>
|
||||
TODO: Query detail page
|
||||
</Route>
|
||||
<Route>
|
||||
<NotFound breadcrumbs={ANALYTICS_BREADCRUMB} product={APP_SEARCH_PLUGIN} />
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
};
|
|
@ -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';
|
||||
|
|
|
@ -5,5 +5,6 @@
|
|||
*/
|
||||
|
||||
export { ANALYTICS_TITLE } from './constants';
|
||||
export { AnalyticsRouter } from './analytics_router';
|
||||
export { AnalyticsChart } from './components';
|
||||
export { convertToChartData } from './utils';
|
||||
|
|
|
@ -103,7 +103,11 @@ export const EngineNav: React.FC = () => {
|
|||
{OVERVIEW_TITLE}
|
||||
</SideNavLink>
|
||||
{canViewEngineAnalytics && (
|
||||
<SideNavLink to={engineRoute + ENGINE_ANALYTICS_PATH} data-test-subj="EngineAnalyticsLink">
|
||||
<SideNavLink
|
||||
to={engineRoute + ENGINE_ANALYTICS_PATH}
|
||||
shouldShowActiveForSubroutes={true}
|
||||
data-test-subj="EngineAnalyticsLink"
|
||||
>
|
||||
{ANALYTICS_TITLE}
|
||||
</SideNavLink>
|
||||
)}
|
||||
|
|
|
@ -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(<EngineRouter />);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="AnalyticsTODO"]')).toHaveLength(1);
|
||||
expect(wrapper.find(AnalyticsRouter)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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 = () => {
|
|||
<Switch>
|
||||
{canViewEngineAnalytics && (
|
||||
<Route path={ENGINE_PATH + ENGINE_ANALYTICS_PATH}>
|
||||
<SetPageChrome trail={[...engineBreadcrumb, ANALYTICS_TITLE]} />
|
||||
<div data-test-subj="AnalyticsTODO">Just testing right now</div>
|
||||
<AnalyticsRouter engineBreadcrumb={engineBreadcrumb} />
|
||||
</Route>
|
||||
)}
|
||||
<Route path={ENGINE_PATH + ENGINE_DOCUMENT_DETAIL_PATH}>
|
||||
|
|
|
@ -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`;
|
||||
|
|
|
@ -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(
|
||||
<NotFound product={APP_SEARCH_PLUGIN} breadcrumbs={['Hello', 'World']} />
|
||||
);
|
||||
|
||||
expect(wrapper.find(SetAppSearchChrome).prop('trail')).toEqual(['Hello', 'World']);
|
||||
});
|
||||
|
||||
it('does not render anything without a valid product', () => {
|
||||
const wrapper = shallow(<NotFound product={undefined as any} />);
|
||||
|
||||
|
|
|
@ -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<NotFoundProps> = ({ product = {} }) => {
|
||||
export const NotFound: React.FC<NotFoundProps> = ({ product = {}, breadcrumbs }) => {
|
||||
const { hasGoldLicense } = useValues(LicensingLogic);
|
||||
const supportUrl = hasGoldLicense ? LICENSED_SUPPORT_URL : product.SUPPORT_URL;
|
||||
|
||||
|
@ -64,7 +67,7 @@ export const NotFound: React.FC<NotFoundProps> = ({ product = {} }) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<SetPageChrome />
|
||||
<SetPageChrome trail={breadcrumbs} />
|
||||
<SendTelemetry action="error" metric="not_found" />
|
||||
|
||||
<EuiPageContent>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue