Removes APM custom breadcrumbs, leaves only chrome.breadcrumbs.set (#29286)

* Removes APM custom breadcrumbs, leaves only chrome.breadcrumbs.set

* Updates breadcrumb tests

* Changes route structure to simplify TS
This commit is contained in:
Jason Rhodes 2019-01-25 07:34:46 -05:00 committed by GitHub
parent 015e831ab0
commit 06a5ce7165
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 293 additions and 245 deletions

View file

@ -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 (
<div className="kuiLocalBreadcrumbs">
{breadcrumbs.map(({ breadcrumb, path, match }, i) => {
const isLast = i === breadcrumbs.length - 1;
return (
<div
key={path}
className="kuiLocalBreadcrumb"
style={{ lineHeight: '29px' }}
>
{isLast ? (
<span
ref={node => {
if (node && document.title !== node.textContent) {
document.title = capitalize(node.textContent);
}
}}
>
{breadcrumb}
</span>
) : (
<a href={`#${match.url}?_g=${_g}&kuery=${kuery}`}>
{breadcrumb}
</a>
)}
</div>
);
})}
</div>
);
}
}
const flatRoutes = flatten(
routes.map(route => (route.switch ? route.routes : route))
);
export default withBreadcrumbs(flatRoutes)(Breadcrumbs);

View file

@ -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<Props> {
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 };

View file

@ -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(
<MemoryRouter initialEntries={[`${route}?_g=myG&kuery=myKuery`]}>
<Breadcrumbs showPluginBreadcrumbs={true} />
<UpdateBreadcrumbs />
</MemoryRouter>
);
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(
<MemoryRouter initialEntries={[`/?_g=myG&kuery=myKuery`]}>
<Breadcrumbs showPluginBreadcrumbs={false} />
</MemoryRouter>
);
expect(wrapper.find('.kuiLocalBreadcrumbs').exists()).toEqual(false);
});
});

View file

@ -1,120 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Breadcrumbs /:serviceName 1`] = `
Array [
<a
href="#/?_g=myG&kuery=myKuery"
>
APM
</a>,
<span>
opbeans-node
</span>,
]
`;
exports[`Breadcrumbs /:serviceName/errors 1`] = `
Array [
<a
href="#/?_g=myG&kuery=myKuery"
>
APM
</a>,
<a
href="#/opbeans-node?_g=myG&kuery=myKuery"
>
opbeans-node
</a>,
<span>
Errors
</span>,
]
`;
exports[`Breadcrumbs /:serviceName/errors/:groupId 1`] = `
Array [
<a
href="#/?_g=myG&kuery=myKuery"
>
APM
</a>,
<a
href="#/opbeans-node?_g=myG&kuery=myKuery"
>
opbeans-node
</a>,
<a
href="#/opbeans-node/errors?_g=myG&kuery=myKuery"
>
Errors
</a>,
<span>
myGroupId
</span>,
]
`;
exports[`Breadcrumbs /:serviceName/transactions 1`] = `
Array [
<a
href="#/?_g=myG&kuery=myKuery"
>
APM
</a>,
<a
href="#/opbeans-node?_g=myG&kuery=myKuery"
>
opbeans-node
</a>,
<span>
Transactions
</span>,
]
`;
exports[`Breadcrumbs /:serviceName/transactions/:transactionType 1`] = `
Array [
<a
href="#/?_g=myG&kuery=myKuery"
>
APM
</a>,
<a
href="#/opbeans-node?_g=myG&kuery=myKuery"
>
opbeans-node
</a>,
<span>
Transactions
</span>,
]
`;
exports[`Breadcrumbs /:serviceName/transactions/:transactionType/:transactionName 1`] = `
Array [
<a
href="#/?_g=myG&kuery=myKuery"
>
APM
</a>,
<a
href="#/:serviceName?_g=myG&kuery=myKuery"
>
:serviceName
</a>,
<a
href="#/:serviceName/transactions?_g=myG&kuery=myKuery"
>
Transactions
</a>,
<span>
my-transaction-name
</span>,
]
`;
exports[`Breadcrumbs Homepage 1`] = `
<span>
APM
</span>
`;

View file

@ -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": <breadcrumb
match={
Object {
"isExact": true,
"params": Object {
"serviceName": "opbeans-node",
},
"path": "/:serviceName",
"url": "/opbeans-node",
}
}
/>,
},
]
`;
exports[`Breadcrumbs /:serviceName/errors 1`] = `
Array [
Object {
"href": "#/?_g=myG&kuery=myKuery",
"text": "APM",
},
Object {
"href": "#/opbeans-node?_g=myG&kuery=myKuery",
"text": <breadcrumb
match={
Object {
"isExact": true,
"params": Object {
"serviceName": "opbeans-node",
},
"path": "/:serviceName",
"url": "/opbeans-node",
}
}
/>,
},
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": <breadcrumb
match={
Object {
"isExact": true,
"params": Object {
"serviceName": "opbeans-node",
},
"path": "/:serviceName",
"url": "/opbeans-node",
}
}
/>,
},
Object {
"href": "#/opbeans-node/errors?_g=myG&kuery=myKuery",
"text": "Errors",
},
Object {
"href": "#/opbeans-node/errors/myGroupId?_g=myG&kuery=myKuery",
"text": <breadcrumb
match={
Object {
"isExact": true,
"params": Object {
"groupId": "myGroupId",
"serviceName": "opbeans-node",
},
"path": "/:serviceName/errors/:groupId",
"url": "/opbeans-node/errors/myGroupId",
}
}
/>,
},
]
`;
exports[`Breadcrumbs /:serviceName/transactions 1`] = `
Array [
Object {
"href": "#/?_g=myG&kuery=myKuery",
"text": "APM",
},
Object {
"href": "#/opbeans-node?_g=myG&kuery=myKuery",
"text": <breadcrumb
match={
Object {
"isExact": true,
"params": Object {
"serviceName": "opbeans-node",
},
"path": "/:serviceName",
"url": "/opbeans-node",
}
}
/>,
},
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": <breadcrumb
match={
Object {
"isExact": true,
"params": Object {
"serviceName": "opbeans-node",
},
"path": "/:serviceName",
"url": "/opbeans-node",
}
}
/>,
},
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": <breadcrumb
match={
Object {
"isExact": true,
"params": Object {
"serviceName": ":serviceName",
},
"path": "/:serviceName",
"url": "/:serviceName",
}
}
/>,
},
Object {
"href": "#/:serviceName/transactions?_g=myG&kuery=myKuery",
"text": "Transactions",
},
Object {
"href": "#/:serviceName/transactions/request/my-transaction-name?_g=myG&kuery=myKuery",
"text": <breadcrumb
match={
Object {
"isExact": true,
"params": Object {
"serviceName": ":serviceName",
"transactionName": "my-transaction-name",
"transactionType": "request",
},
"path": "/:serviceName/transactions/:transactionType/:transactionName",
"url": "/:serviceName/transactions/request/my-transaction-name",
}
}
/>,
},
]
`;
exports[`Breadcrumbs Homepage 1`] = `
Array [
Object {
"href": "#/?_g=myG&kuery=myKuery",
"text": "APM",
},
]
`;

View file

@ -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 (
<MainContainer>
<UpdateBreadcrumbs />
<Route component={ConnectRouterToRedux} />
<Route component={ScrollToTopOnPathChange} />
{routes.map((route, i) => {
return route.switch ? (
return route.switchRoutes ? (
<Switch key={i}>
{route.routes.map((route, i) => (
{route.switchRoutes.map((route, i) => (
<Route key={i} {...route} />
))}
</Switch>

View file

@ -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<RouteParams>) => (
<Redirect
@ -36,7 +43,7 @@ const renderAsRedirectTo = (to: string) => {
);
};
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',

View file

@ -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(
<Router history={history}>
<Breadcrumbs showPluginBreadcrumbs={showPluginBreadcrumbs} />
</Router>,
document.getElementById('react-apm-breadcrumbs')
);
ReactDOM.render(
<I18nProvider>
<Provider store={store}>

View file

@ -1,11 +1,5 @@
<div ng-controller="TimePickerController">
<kbn-top-nav name="apm" config="topNavMenu">
<div data-transclude-slots>
<div data-transclude-slot="topLeftCorner">
<div id="react-apm-breadcrumbs"></div>
</div>
</div>
</kbn-top-nav>
<div ng-controller="TimePickerController" id="kibana-angular-template">
<kbn-top-nav name="apm" config="topNavMenu"></kbn-top-nav>
</div>
<div id="react-apm-root" style="overflow-x: auto;"></div>

View file

@ -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();