[Enterprise Search] Fix cross-application links & breadcrumbs (#79530)

* Update our cross-app links to correctly include Kibana basePaths

- see https://www.elastic.co/guide/en/kibana/master/kibana-navigation.html
- for this change, we need to pass in the `http` lib as another dependency alongside `history` to createHelper

* Update helpers using createHref to pass new expected dependency

- Note that a lot of their createHref tests did not functionally change in output because Kibana mocks an empty basePath ('/') in tests
This commit is contained in:
Constance 2020-10-05 12:48:07 -07:00 committed by GitHub
parent fe13030979
commit 1cf7f783cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 52 additions and 14 deletions

View file

@ -6,7 +6,10 @@
import { resetContext } from 'kea';
import { mockKibanaValues } from '../../__mocks__';
import { mockKibanaValues, mockHttpValues } from '../../__mocks__';
jest.mock('../http', () => ({
HttpLogic: { values: { http: mockHttpValues.http } },
}));
import { KibanaLogic, mountKibanaLogic } from './kibana_logic';

View file

@ -10,6 +10,7 @@ import { FC } from 'react';
import { History } from 'history';
import { ApplicationStart, ChromeBreadcrumb } from 'src/core/public';
import { HttpLogic } from '../http';
import { createHref, ICreateHrefOptions } from '../react_router_helpers';
interface IKibanaLogicProps {
@ -31,7 +32,8 @@ export const KibanaLogic = kea<MakeLogicType<IKibanaValues>>({
history: [props.history, {}],
navigateToUrl: [
(url: string, options?: ICreateHrefOptions) => {
const href = createHref(url, props.history, options);
const deps = { history: props.history, http: HttpLogic.values.http };
const href = createHref(url, deps, options);
return props.navigateToUrl(href);
},
{},

View file

@ -7,7 +7,8 @@
import { useValues } from 'kea';
import { EuiBreadcrumb } from '@elastic/eui';
import { KibanaLogic } from '../../shared/kibana';
import { KibanaLogic } from '../kibana';
import { HttpLogic } from '../http';
import {
ENTERPRISE_SEARCH_PLUGIN,
@ -66,12 +67,13 @@ export const useGenerateBreadcrumbs = (trail: TBreadcrumbTrail): TBreadcrumbs =>
export const useEuiBreadcrumbs = (breadcrumbs: TBreadcrumbs): EuiBreadcrumb[] => {
const { navigateToUrl, history } = useValues(KibanaLogic);
const { http } = useValues(HttpLogic);
return breadcrumbs.map(({ text, path, shouldNotCreateHref }) => {
const breadcrumb: EuiBreadcrumb = { text };
if (path) {
breadcrumb.href = createHref(path, history, { shouldNotCreateHref });
breadcrumb.href = createHref(path, { history, http }, { shouldNotCreateHref });
breadcrumb.onClick = (event) => {
if (letBrowserHandleEvent(event)) return;
event.preventDefault();

View file

@ -4,16 +4,33 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { httpServiceMock } from 'src/core/public/mocks';
import { mockHistory } from '../../__mocks__';
import { createHref } from './';
describe('createHref', () => {
const dependencies = {
history: mockHistory,
http: httpServiceMock.createSetupContract(),
};
it('generates a path with the React Router basename included', () => {
expect(createHref('/test', mockHistory)).toEqual('/app/enterprise_search/test');
expect(createHref('/test', dependencies)).toEqual('/app/enterprise_search/test');
});
it('does not include the basename if shouldNotCreateHref is passed', () => {
expect(createHref('/test', mockHistory, { shouldNotCreateHref: true })).toEqual('/test');
describe('shouldNotCreateHref', () => {
const options = { shouldNotCreateHref: true };
it('does not include the router basename,', () => {
expect(createHref('/test', dependencies, options)).toEqual('/test');
});
it('does include the Kibana basepath,', () => {
const http = httpServiceMock.createSetupContract({ basePath: '/xyz/s/custom-space' });
const basePathDeps = { ...dependencies, http };
expect(createHref('/test', basePathDeps, options)).toEqual('/xyz/s/custom-space/test');
});
});
});

View file

@ -5,23 +5,35 @@
*/
import { History } from 'history';
import { HttpSetup } from 'src/core/public';
/**
* This helper uses React Router's createHref function to generate links with router basenames accounted for.
* This helper uses React Router's createHref function to generate links with router basenames included.
* For example, if we perform navigateToUrl('/engines') within App Search, we expect the app basename
* to be taken into account to be intelligently routed to '/app/enterprise_search/app_search/engines'.
* to be taken into account & intelligently routed to '/app/enterprise_search/app_search/engines'.
*
* This helper accomplishes that, while still giving us an escape hatch for navigation *between* apps.
* For example, if we want to navigate the user from App Search to Enterprise Search we could
* navigateToUrl('/app/enterprise_search', { shouldNotCreateHref: true })
*
* Said escape hatch should still contain all of Kibana's basepaths - for example,
* 'localhost:5601/xyz' when developing locally, or '/s/some-custom-space/' for space basepaths.
* See: https://www.elastic.co/guide/en/kibana/master/kibana-navigation.html
*
* Links completely outside of Kibana should not use our React Router helpers or navigateToUrl.
*/
interface ICreateHrefDeps {
history: History;
http: HttpSetup;
}
export interface ICreateHrefOptions {
shouldNotCreateHref?: boolean;
}
export const createHref = (
path: string,
history: History,
options?: ICreateHrefOptions
{ history, http }: ICreateHrefDeps,
{ shouldNotCreateHref }: ICreateHrefOptions = {}
): string => {
return options?.shouldNotCreateHref ? path : history.createHref({ pathname: path });
return shouldNotCreateHref ? http.basePath.prepend(path) : history.createHref({ pathname: path });
};

View file

@ -8,7 +8,8 @@ import React from 'react';
import { useValues } from 'kea';
import { EuiLink, EuiButton, EuiButtonProps, EuiLinkAnchorProps } from '@elastic/eui';
import { KibanaLogic } from '../../shared/kibana';
import { KibanaLogic } from '../kibana';
import { HttpLogic } from '../http';
import { letBrowserHandleEvent, createHref } from './';
/**
@ -33,9 +34,10 @@ export const EuiReactRouterHelper: React.FC<IEuiReactRouterProps> = ({
children,
}) => {
const { navigateToUrl, history } = useValues(KibanaLogic);
const { http } = useValues(HttpLogic);
// Generate the correct link href (with basename etc. accounted for)
const href = createHref(to, history, { shouldNotCreateHref });
const href = createHref(to, { history, http }, { shouldNotCreateHref });
const reactRouterLinkClick = (event: React.MouseEvent) => {
if (onClick) onClick(); // Run any passed click events (e.g. telemetry)