diff --git a/x-pack/plugins/apm/public/components/app/Main/Breadcrumbs.js b/x-pack/plugins/apm/public/components/app/Main/Breadcrumbs.js deleted file mode 100644 index 74870aef9e860..0000000000000 --- a/x-pack/plugins/apm/public/components/app/Main/Breadcrumbs.js +++ /dev/null @@ -1,80 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { withBreadcrumbs } from 'react-router-breadcrumbs-hoc'; -import { flatten, capitalize } from 'lodash'; -import chrome from 'ui/chrome'; -import { toQuery } from '../../shared/Links/url_helpers'; -import { routes } from './routeConfig'; - -class Breadcrumbs extends React.Component { - updateHeaderBreadcrumbs() { - const { _g = '', kuery = '' } = toQuery(this.props.location.search); - const breadcrumbs = this.props.breadcrumbs.map(({ breadcrumb, match }) => ({ - text: breadcrumb, - href: `#${match.url}?_g=${_g}&kuery=${kuery}` - })); - - chrome.breadcrumbs.set(breadcrumbs); - } - - componentDidMount() { - this.updateHeaderBreadcrumbs(); - } - - componentDidUpdate() { - this.updateHeaderBreadcrumbs(); - } - - render() { - const { breadcrumbs, location, showPluginBreadcrumbs } = this.props; - const { _g = '', kuery = '' } = toQuery(location.search); - - // If we don't display plugin breadcrumbs, render null, but continue - // to push updates to header. - if (!showPluginBreadcrumbs) { - return null; - } - - return ( -
- {breadcrumbs.map(({ breadcrumb, path, match }, i) => { - const isLast = i === breadcrumbs.length - 1; - return ( -
- {isLast ? ( - { - if (node && document.title !== node.textContent) { - document.title = capitalize(node.textContent); - } - }} - > - {breadcrumb} - - ) : ( - - {breadcrumb} - - )} -
- ); - })} -
- ); - } -} - -const flatRoutes = flatten( - routes.map(route => (route.switch ? route.routes : route)) -); - -export default withBreadcrumbs(flatRoutes)(Breadcrumbs); diff --git a/x-pack/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.ts b/x-pack/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.ts new file mode 100644 index 0000000000000..45fe37e9bf8c1 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.ts @@ -0,0 +1,58 @@ +/* + * 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 { Location } from 'history'; +import { flatten } from 'lodash'; +import React from 'react'; +// @ts-ignore +import { withBreadcrumbs } from 'react-router-breadcrumbs-hoc'; +import chrome from 'ui/chrome'; +import { toQuery } from '../../shared/Links/url_helpers'; +import { routes } from './routeConfig'; + +interface Props { + location: Location; + breadcrumbs: Array<{ + breadcrumb: any; + match: { + url: string; + }; + }>; +} + +class UpdateBreadcrumbsComponent extends React.Component { + public updateHeaderBreadcrumbs() { + const { _g = '', kuery = '' } = toQuery(this.props.location.search); + const breadcrumbs = this.props.breadcrumbs.map(({ breadcrumb, match }) => ({ + text: breadcrumb, + href: `#${match.url}?_g=${_g}&kuery=${kuery}` + })); + + chrome.breadcrumbs.set(breadcrumbs); + } + + public componentDidMount() { + this.updateHeaderBreadcrumbs(); + } + + public componentDidUpdate() { + this.updateHeaderBreadcrumbs(); + } + + public render() { + return null; + } +} + +const flatRoutes = flatten( + routes.map(route => (route.switchRoutes ? route.switchRoutes : route)) +); + +const UpdateBreadcrumbs = withBreadcrumbs(flatRoutes)( + UpdateBreadcrumbsComponent +); + +export { UpdateBreadcrumbs }; diff --git a/x-pack/plugins/apm/public/components/app/Main/__test__/Breadcrumbs.test.js b/x-pack/plugins/apm/public/components/app/Main/__test__/UpdateBreadcrumbs.test.js similarity index 76% rename from x-pack/plugins/apm/public/components/app/Main/__test__/Breadcrumbs.test.js rename to x-pack/plugins/apm/public/components/app/Main/__test__/UpdateBreadcrumbs.test.js index be57500928f4b..28337f771255d 100644 --- a/x-pack/plugins/apm/public/components/app/Main/__test__/Breadcrumbs.test.js +++ b/x-pack/plugins/apm/public/components/app/Main/__test__/UpdateBreadcrumbs.test.js @@ -7,15 +7,14 @@ import React from 'react'; import { mount } from 'enzyme'; import { MemoryRouter } from 'react-router-dom'; - -import Breadcrumbs from '../Breadcrumbs'; -import { toJson } from '../../../../utils/testHelpers'; +import { UpdateBreadcrumbs } from '../UpdateBreadcrumbs'; +import chrome from 'ui/chrome'; jest.mock( 'ui/chrome', () => ({ breadcrumbs: { - set: () => {} + set: jest.fn() }, getBasePath: () => `/some/base/path`, getUiSettingsClient: () => { @@ -37,17 +36,20 @@ jest.mock( ); function expectBreadcrumbToMatchSnapshot(route) { - const wrapper = mount( + mount( - + ); - expect( - toJson(wrapper.find('.kuiLocalBreadcrumb').children()) - ).toMatchSnapshot(); + expect(chrome.breadcrumbs.set).toHaveBeenCalledTimes(1); + expect(chrome.breadcrumbs.set.mock.calls[0][0]).toMatchSnapshot(); } describe('Breadcrumbs', () => { + beforeEach(() => { + chrome.breadcrumbs.set.mockReset(); + }); + it('Homepage', () => { expectBreadcrumbToMatchSnapshot('/'); }); @@ -77,13 +79,4 @@ describe('Breadcrumbs', () => { '/:serviceName/transactions/request/my-transaction-name' ); }); - - it('does not render breadcrumbs when showPluginBreadcrumbs = false', () => { - const wrapper = mount( - - - - ); - expect(wrapper.find('.kuiLocalBreadcrumbs').exists()).toEqual(false); - }); }); diff --git a/x-pack/plugins/apm/public/components/app/Main/__test__/__snapshots__/Breadcrumbs.test.js.snap b/x-pack/plugins/apm/public/components/app/Main/__test__/__snapshots__/Breadcrumbs.test.js.snap deleted file mode 100644 index 73aded84664bb..0000000000000 --- a/x-pack/plugins/apm/public/components/app/Main/__test__/__snapshots__/Breadcrumbs.test.js.snap +++ /dev/null @@ -1,120 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Breadcrumbs /:serviceName 1`] = ` -Array [ - - APM - , - - opbeans-node - , -] -`; - -exports[`Breadcrumbs /:serviceName/errors 1`] = ` -Array [ - - APM - , - - opbeans-node - , - - Errors - , -] -`; - -exports[`Breadcrumbs /:serviceName/errors/:groupId 1`] = ` -Array [ - - APM - , - - opbeans-node - , - - Errors - , - - myGroupId - , -] -`; - -exports[`Breadcrumbs /:serviceName/transactions 1`] = ` -Array [ - - APM - , - - opbeans-node - , - - Transactions - , -] -`; - -exports[`Breadcrumbs /:serviceName/transactions/:transactionType 1`] = ` -Array [ - - APM - , - - opbeans-node - , - - Transactions - , -] -`; - -exports[`Breadcrumbs /:serviceName/transactions/:transactionType/:transactionName 1`] = ` -Array [ - - APM - , - - :serviceName - , - - Transactions - , - - my-transaction-name - , -] -`; - -exports[`Breadcrumbs Homepage 1`] = ` - - APM - -`; diff --git a/x-pack/plugins/apm/public/components/app/Main/__test__/__snapshots__/UpdateBreadcrumbs.test.js.snap b/x-pack/plugins/apm/public/components/app/Main/__test__/__snapshots__/UpdateBreadcrumbs.test.js.snap new file mode 100644 index 0000000000000..681c20d1424df --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/Main/__test__/__snapshots__/UpdateBreadcrumbs.test.js.snap @@ -0,0 +1,207 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Breadcrumbs /:serviceName 1`] = ` +Array [ + Object { + "href": "#/?_g=myG&kuery=myKuery", + "text": "APM", + }, + Object { + "href": "#/opbeans-node?_g=myG&kuery=myKuery", + "text": , + }, +] +`; + +exports[`Breadcrumbs /:serviceName/errors 1`] = ` +Array [ + Object { + "href": "#/?_g=myG&kuery=myKuery", + "text": "APM", + }, + Object { + "href": "#/opbeans-node?_g=myG&kuery=myKuery", + "text": , + }, + Object { + "href": "#/opbeans-node/errors?_g=myG&kuery=myKuery", + "text": "Errors", + }, +] +`; + +exports[`Breadcrumbs /:serviceName/errors/:groupId 1`] = ` +Array [ + Object { + "href": "#/?_g=myG&kuery=myKuery", + "text": "APM", + }, + Object { + "href": "#/opbeans-node?_g=myG&kuery=myKuery", + "text": , + }, + Object { + "href": "#/opbeans-node/errors?_g=myG&kuery=myKuery", + "text": "Errors", + }, + Object { + "href": "#/opbeans-node/errors/myGroupId?_g=myG&kuery=myKuery", + "text": , + }, +] +`; + +exports[`Breadcrumbs /:serviceName/transactions 1`] = ` +Array [ + Object { + "href": "#/?_g=myG&kuery=myKuery", + "text": "APM", + }, + Object { + "href": "#/opbeans-node?_g=myG&kuery=myKuery", + "text": , + }, + Object { + "href": "#/opbeans-node/transactions?_g=myG&kuery=myKuery", + "text": "Transactions", + }, +] +`; + +exports[`Breadcrumbs /:serviceName/transactions/:transactionType 1`] = ` +Array [ + Object { + "href": "#/?_g=myG&kuery=myKuery", + "text": "APM", + }, + Object { + "href": "#/opbeans-node?_g=myG&kuery=myKuery", + "text": , + }, + Object { + "href": "#/opbeans-node/transactions?_g=myG&kuery=myKuery", + "text": "Transactions", + }, +] +`; + +exports[`Breadcrumbs /:serviceName/transactions/:transactionType/:transactionName 1`] = ` +Array [ + Object { + "href": "#/?_g=myG&kuery=myKuery", + "text": "APM", + }, + Object { + "href": "#/:serviceName?_g=myG&kuery=myKuery", + "text": , + }, + Object { + "href": "#/:serviceName/transactions?_g=myG&kuery=myKuery", + "text": "Transactions", + }, + Object { + "href": "#/:serviceName/transactions/request/my-transaction-name?_g=myG&kuery=myKuery", + "text": , + }, +] +`; + +exports[`Breadcrumbs Homepage 1`] = ` +Array [ + Object { + "href": "#/?_g=myG&kuery=myKuery", + "text": "APM", + }, +] +`; diff --git a/x-pack/plugins/apm/public/components/app/Main/index.js b/x-pack/plugins/apm/public/components/app/Main/index.js index f08a59ec020fe..865bf756f7663 100644 --- a/x-pack/plugins/apm/public/components/app/Main/index.js +++ b/x-pack/plugins/apm/public/components/app/Main/index.js @@ -11,6 +11,7 @@ import { routes } from './routeConfig'; import ScrollToTopOnPathChange from './ScrollToTopOnPathChange'; import { px, units, unit, topNavHeight } from '../../../style/variables'; import ConnectRouterToRedux from '../../shared/ConnectRouterToRedux'; +import { UpdateBreadcrumbs } from './UpdateBreadcrumbs'; const MainContainer = styled.div` min-width: ${px(unit * 50)}; @@ -21,12 +22,13 @@ const MainContainer = styled.div` export default function Main() { return ( + {routes.map((route, i) => { - return route.switch ? ( + return route.switchRoutes ? ( - {route.routes.map((route, i) => ( + {route.switchRoutes.map((route, i) => ( ))} diff --git a/x-pack/plugins/apm/public/components/app/Main/routeConfig.tsx b/x-pack/plugins/apm/public/components/app/Main/routeConfig.tsx index bc59dd934fa08..bbdb1076d9426 100644 --- a/x-pack/plugins/apm/public/components/app/Main/routeConfig.tsx +++ b/x-pack/plugins/apm/public/components/app/Main/routeConfig.tsx @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; -import { Redirect, RouteComponentProps } from 'react-router-dom'; +import { Redirect, RouteComponentProps, RouteProps } from 'react-router-dom'; import { legacyDecodeURIComponent } from 'x-pack/plugins/apm/public/components/shared/Links/url_helpers'; import { StringMap } from '../../../../typings/common'; // @ts-ignore @@ -25,6 +25,13 @@ interface RouteParams { serviceName: string; } +type BreadcrumbFunction = (args: BreadcrumbArgs) => string | null; + +interface Route extends RouteProps { + switchRoutes?: Route[]; + breadcrumb?: string | BreadcrumbFunction | null; +} + const renderAsRedirectTo = (to: string) => { return ({ location }: RouteComponentProps) => ( { ); }; -export const routes = [ +export const routes: Route[] = [ { exact: true, path: '/', @@ -58,8 +65,7 @@ export const routes = [ }) }, { - switch: true, - routes: [ + switchRoutes: [ { exact: true, path: '/invalid-license', diff --git a/x-pack/plugins/apm/public/index.js b/x-pack/plugins/apm/public/index.js index 77a91e3eae43c..74b246fab9488 100644 --- a/x-pack/plugins/apm/public/index.js +++ b/x-pack/plugins/apm/public/index.js @@ -18,7 +18,6 @@ import './style/global_overrides.css'; import template from './templates/index.html'; import Main from './components/app/Main'; -import Breadcrumbs from './components/app/Main/Breadcrumbs'; import { initTimepicker } from './utils/timepicker'; import configureStore from './store/config/configureStore'; @@ -33,17 +32,6 @@ chrome.setRootTemplate(template); const store = configureStore(); initTimepicker(history, store.dispatch).then(() => { - const showPluginBreadcrumbs = !chrome - .getUiSettingsClient() - .get('k7design', false); - - ReactDOM.render( - - - , - document.getElementById('react-apm-breadcrumbs') - ); - ReactDOM.render( diff --git a/x-pack/plugins/apm/public/templates/index.html b/x-pack/plugins/apm/public/templates/index.html index af514d69098b8..b38dee3e344aa 100644 --- a/x-pack/plugins/apm/public/templates/index.html +++ b/x-pack/plugins/apm/public/templates/index.html @@ -1,11 +1,5 @@ -
- -
-
-
-
-
-
+
+
diff --git a/x-pack/plugins/apm/public/utils/timepicker/index.js b/x-pack/plugins/apm/public/utils/timepicker/index.js index 505682d177db5..b0c35cd43eb65 100644 --- a/x-pack/plugins/apm/public/utils/timepicker/index.js +++ b/x-pack/plugins/apm/public/utils/timepicker/index.js @@ -15,7 +15,7 @@ let currentInterval; // hack to wait for angular template to be ready const waitForAngularReady = new Promise(resolve => { const checkInterval = setInterval(() => { - const hasElm = !!document.querySelector('#react-apm-breadcrumbs'); + const hasElm = !!document.querySelector('#kibana-angular-template'); if (hasElm) { clearInterval(checkInterval); resolve();