From e1d287c11b612c9b71effb5319219c123ea9e341 Mon Sep 17 00:00:00 2001 From: Constance Chen Date: Thu, 16 Apr 2020 23:54:51 -0700 Subject: [PATCH] Implement Kibana Chrome breadcrumbs - create shared helper (WS will presumably also want this) for generating EUI breadcrumb objects with React Router links+click behavior - create React component that calls chrome.setBreadcrumbs on page mount - clean up type definitions - move app-wide props to IAppSearchProps and update most pages/views to simply import it instead of calling their own definitions --- .../components/empty_states/empty_state.tsx | 8 ++- .../components/empty_states/error_state.tsx | 8 ++- .../components/empty_states/loading_state.tsx | 8 ++- .../components/empty_states/no_user_state.tsx | 8 ++- .../engine_overview/engine_overview.tsx | 18 ++++--- .../components/setup_guide/setup_guide.tsx | 5 +- .../public/applications/app_search/index.tsx | 7 ++- .../public/applications/index.tsx | 6 ++- .../generate_breadcrumbs.ts | 50 +++++++++++++++++++ .../kibana_breadcrumbs/index.ts} | 6 +-- .../kibana_breadcrumbs/set_breadcrumbs.tsx | 38 ++++++++++++++ 11 files changed, 140 insertions(+), 22 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/generate_breadcrumbs.ts rename x-pack/plugins/enterprise_search/public/applications/{app_search/components/empty_states/types.ts => shared/kibana_breadcrumbs/index.ts} (55%) create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/set_breadcrumbs.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/empty_state.tsx index c120166a197b4..c2672ec3be085 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/empty_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/empty_state.tsx @@ -7,14 +7,18 @@ import React from 'react'; import { EuiPage, EuiPageBody, EuiPageContent, EuiEmptyPrompt, EuiButton } from '@elastic/eui'; +import { SetAppSearchBreadcrumbs as SetBreadcrumbs } from '../../../shared/kibana_breadcrumbs'; +import { IAppSearchProps } from '../../index'; + import { EngineOverviewHeader } from '../engine_overview_header'; -import { IEmptyStatesProps } from './types'; import './empty_states.scss'; -export const EmptyState: React.FC = ({ appSearchUrl }) => { +export const EmptyState: React.FC = ({ appSearchUrl, setBreadcrumbs }) => { return ( + + diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/error_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/error_state.tsx index b2ff79b47c7a6..3eec392749c2f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/error_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/error_state.tsx @@ -6,16 +6,20 @@ import React from 'react'; import { EuiPage, EuiPageBody, EuiPageContent, EuiEmptyPrompt, EuiCode } from '@elastic/eui'; + import { EuiButton } from '../../../shared/react_router_helpers'; +import { SetAppSearchBreadcrumbs as SetBreadcrumbs } from '../../../shared/kibana_breadcrumbs'; +import { IAppSearchProps } from '../../index'; import { EngineOverviewHeader } from '../engine_overview_header'; -import { IEmptyStatesProps } from './types'; import './empty_states.scss'; -export const ErrorState: ReactFC = ({ appSearchUrl }) => { +export const ErrorState: ReactFC = ({ appSearchUrl, setBreadcrumbs }) => { return ( + + diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/loading_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/loading_state.tsx index 44d9e5d7ea64c..426716030adc4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/loading_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/loading_state.tsx @@ -7,14 +7,18 @@ import React from 'react'; import { EuiPage, EuiPageBody, EuiPageContent, EuiSpacer, EuiLoadingContent } from '@elastic/eui'; +import { SetAppSearchBreadcrumbs as SetBreadcrumbs } from '../../../shared/kibana_breadcrumbs'; +import { IAppSearchProps } from '../../index'; + import { EngineOverviewHeader } from '../engine_overview_header'; -import { IEmptyStatesProps } from './types'; import './empty_states.scss'; -export const LoadingState: React.FC = ({ appSearchUrl }) => { +export const LoadingState: React.FC = ({ appSearchUrl, setBreadcrumbs }) => { return ( + + diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/no_user_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/no_user_state.tsx index 0e41586d95aff..cc3fc130a7b01 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/no_user_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/no_user_state.tsx @@ -7,17 +7,21 @@ import React from 'react'; import { EuiPage, EuiPageBody, EuiPageContent, EuiEmptyPrompt, EuiCode } from '@elastic/eui'; -import { IEmptyStatesProps } from './types'; +import { SetAppSearchBreadcrumbs as SetBreadcrumbs } from '../../../shared/kibana_breadcrumbs'; +import { IAppSearchProps } from '../../index'; + import { EngineOverviewHeader } from '../engine_overview_header'; import { getUserName } from '../../utils/get_username'; import './empty_states.scss'; -export const NoUserState: React.FC = ({ appSearchUrl }) => { +export const NoUserState: React.FC = ({ appSearchUrl, setBreadcrumbs }) => { const username = getUserName(); return ( + + diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx index c149b61f67d27..c7c06bfb05e72 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx @@ -15,6 +15,9 @@ import { EuiSpacer, } from '@elastic/eui'; +import { SetAppSearchBreadcrumbs as SetBreadcrumbs } from '../../../shared/kibana_breadcrumbs'; +import { IAppSearchProps } from '../../index'; + import EnginesIcon from '../../assets/engine.svg'; import MetaEnginesIcon from '../../assets/meta_engine.svg'; @@ -24,12 +27,9 @@ import { EngineTable } from './engine_table'; import './engine_overview.scss'; -interface IEngineOverviewProps { - appSearchUrl: string; - http(); -} +export const EngineOverview: ReactFC = props => { + const { http, appSearchUrl } = props; -export const EngineOverview: ReactFC = ({ http, ...props }) => { const [isLoading, setIsLoading] = useState(true); const [hasNoAccount, setHasNoAccount] = useState(false); const [hasErrorConnecting, setHasErrorConnecting] = useState(false); @@ -95,8 +95,10 @@ export const EngineOverview: ReactFC = ({ http, ...props } return ( + + - + @@ -115,7 +117,7 @@ export const EngineOverview: ReactFC = ({ http, ...props } pageIndex: enginesPage - 1, onPaginate: setEnginesPage, }} - appSearchUrl={props.appSearchUrl} + appSearchUrl={appSearchUrl} /> @@ -138,7 +140,7 @@ export const EngineOverview: ReactFC = ({ http, ...props } pageIndex: metaEnginesPage - 1, onPaginate: setMetaEnginesPage, }} - appSearchUrl={props.appSearchUrl} + appSearchUrl={appSearchUrl} /> diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/setup_guide/setup_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/setup_guide/setup_guide.tsx index 107107d6e52ef..b91fbdf1a19f3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/setup_guide/setup_guide.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/setup_guide/setup_guide.tsx @@ -23,12 +23,15 @@ import { EuiCodeBlock, } from '@elastic/eui'; +import { SetAppSearchBreadcrumbs as SetBreadcrumbs } from '../../../shared/kibana_breadcrumbs'; + import GettingStarted from '../../assets/getting_started.png'; import './setup_guide.scss'; -export const SetupGuide: React.FC<> = () => { +export const SetupGuide: React.FC<> = props => { return ( + Setup Guide diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx index 70cf40e6b9db9..b7cd6e27696d6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx @@ -7,11 +7,16 @@ import React from 'react'; import { Route, Redirect } from 'react-router-dom'; +import { HttpHandler } from 'src/core/public'; +import { TSetBreadcrumbs } from '../../../shared/kibana_breadcrumbs'; + import { SetupGuide } from './components/setup_guide'; import { EngineOverview } from './components/engine_overview'; -interface IAppSearchProps { +export interface IAppSearchProps { appSearchUrl?: string; + http(): HttpHandler; + setBreadCrumbs(): TSetBreadcrumbs; } export const AppSearch: React.FC = props => ( diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx index 73ad2c10745c7..3a7f5b37a2861 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx @@ -22,7 +22,11 @@ export const renderApp = (core: CoreStart, params: AppMountParams, config: Clien - + , params.element diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/generate_breadcrumbs.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/generate_breadcrumbs.ts new file mode 100644 index 0000000000000..9aab47e51433a --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/generate_breadcrumbs.ts @@ -0,0 +1,50 @@ +/* + * 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 { Breadcrumb as EuiBreadcrumb } from '@elastic/eui'; +import { History } from 'history'; + +import { letBrowserHandleEvent } from '../react_router_helpers'; + +/** + * Generate React-Router-friendly EUI breadcrumb objects + * https://elastic.github.io/eui/#/navigation/breadcrumbs + */ + +interface IGenerateBreadcrumbProps { + text: string; + path: string; + history: History; +} + +export const generateBreadcrumb = ({ text, path, history }: IGenerateBreadcrumbProps) => ({ + text, + href: history.createHref({ pathname: path }), + onClick: event => { + if (letBrowserHandleEvent(event)) return; + event.preventDefault(); + history.push(path); + }, +}); + +/** + * Product-specific breadcrumb helpers + */ + +type TBreadcrumbs = EuiBreadcrumb[] | []; + +export const enterpriseSearchBreadcrumbs = (history: History) => ( + breadcrumbs: TBreadcrumbs = [] +) => [ + generateBreadcrumb({ text: 'Enterprise Search', path: '/', history }), + ...breadcrumbs.map(({ text, path }) => generateBreadcrumb({ text, path, history })), +]; + +export const appSearchBreadcrumbs = (history: History) => (breadcrumbs: TBreadcrumbs = []) => + enterpriseSearchBreadcrumbs(history)([ + { text: 'App Search', path: '/app_search' }, + ...breadcrumbs, + ]); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/types.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/index.ts similarity index 55% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/types.ts rename to x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/index.ts index e0a51a80069c1..cf8bbbc593f2f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/index.ts @@ -4,6 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export interface IEmptyStatesProps { - appSearchUrl: string; -} +export { enterpriseSearchBreadcrumbs } from './generate_breadcrumbs'; +export { appSearchBreadcrumbs } from './generate_breadcrumbs'; +export { SetAppSearchBreadcrumbs } from './set_breadcrumbs'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/set_breadcrumbs.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/set_breadcrumbs.tsx new file mode 100644 index 0000000000000..7ce1ba1268f98 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/set_breadcrumbs.tsx @@ -0,0 +1,38 @@ +/* + * 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, { useEffect } from 'react'; +import { useHistory } from 'react-router-dom'; +import { Breadcrumb as EuiBreadcrumb } from '@elastic/eui'; +import { appSearchBreadcrumbs } from './generate_breadcrumbs'; + +/** + * Small on-mount helper for setting Kibana's chrome breadcrumbs on any App Search view + * @see https://github.com/elastic/kibana/blob/master/src/core/public/chrome/chrome_service.tsx + */ + +export type TSetBreadcrumbs = (breadcrumbs: EuiBreadcrumb[]) => void; + +interface ISetBreadcrumbsProps { + text: string; + setBreadcrumbs(): setBreadcrumbs; + isRoot?: boolean; +} + +export const SetAppSearchBreadcrumbs: React.FC = ({ + text, + setBreadcrumbs, + isRoot, +}) => { + const history = useHistory(); + const crumb = isRoot ? [] : [{ text, path: history.location.pathname }]; + + useEffect(() => { + setBreadcrumbs(appSearchBreadcrumbs(history)(crumb)); + }, []); // eslint-disable-line + + return null; +};