diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.test.ts index 4d51362a7e11b0..a763518d30b992 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.test.ts @@ -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'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts index 9519a62ac352bf..89ed07f302b035 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts @@ -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>({ 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); }, {}, diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts index 4882c0d33ae553..e22334aeea3712 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts @@ -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(); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.test.ts index 5f96beeb42ae4f..353c24a342c175 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.test.ts @@ -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'); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.ts b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.ts index cc8279c80a0920..aa2f09a195c8dd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.ts @@ -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 }); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx index e0aa5afdf38c11..f9f6ec54e8832e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx @@ -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 = ({ 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)