diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_context.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_context.mock.ts index ef3bf54053b5c..890072ab42eb9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_context.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_context.mock.ts @@ -13,6 +13,7 @@ import { ExternalUrl } from '../shared/enterprise_search_url'; */ export const mockKibanaContext = { http: httpServiceMock.createSetupContract(), + navigateToUrl: jest.fn(), setBreadcrumbs: jest.fn(), setDocTitle: jest.fn(), config: { host: 'http://localhost:3002' }, 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 7ebd35ff35ee1..5856a13bf75b7 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 @@ -36,7 +36,6 @@ export const AppSearch: React.FC = () => { - {/* Kibana displays a blank page on redirect if this isn't included */} ); @@ -50,8 +49,7 @@ export const AppSearch: React.FC = () => { }> - {/* For some reason a Redirect to /engines just doesn't work here - it shows a blank page */} - + diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx index f3ccbc126ae62..1b1f9ae43e7c1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx @@ -9,7 +9,13 @@ import ReactDOM from 'react-dom'; import { Router } from 'react-router-dom'; import { I18nProvider } from '@kbn/i18n/react'; -import { CoreStart, AppMountParameters, HttpSetup, ChromeBreadcrumb } from 'src/core/public'; +import { + AppMountParameters, + CoreStart, + ApplicationStart, + HttpSetup, + ChromeBreadcrumb, +} from 'src/core/public'; import { ClientConfigType, ClientData, PluginsSetup } from '../plugin'; import { LicenseProvider } from './shared/licensing'; import { IExternalUrl } from './shared/enterprise_search_url'; @@ -18,6 +24,7 @@ export interface IKibanaContext { config: { host?: string }; externalUrl: IExternalUrl; http: HttpSetup; + navigateToUrl: ApplicationStart['navigateToUrl']; setBreadcrumbs(crumbs: ChromeBreadcrumb[]): void; setDocTitle(title: string): void; } @@ -44,6 +51,7 @@ export const renderApp = ( value={{ config, http: core.http, + navigateToUrl: core.application.navigateToUrl, externalUrl: data.externalUrl, setBreadcrumbs: core.chrome.setBreadcrumbs, setDocTitle: core.chrome.docTitle.change, diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.test.ts index 0f34bbb6b65bc..9e86b239432a7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.test.ts @@ -4,140 +4,121 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - generateBreadcrumb, - appSearchBreadcrumbs, - enterpriseSearchBreadcrumbs, - workplaceSearchBreadcrumbs, -} from './generate_breadcrumbs'; - -import { mockHistory as mockHistoryUntyped } from '../../__mocks__'; -const mockHistory = mockHistoryUntyped as any; +import '../../__mocks__/shallow_usecontext.mock'; +import '../../__mocks__/react_router_history.mock'; +import { mockKibanaContext, mockHistory } from '../../__mocks__'; jest.mock('../react_router_helpers', () => ({ letBrowserHandleEvent: jest.fn(() => false) })); import { letBrowserHandleEvent } from '../react_router_helpers'; -describe('generateBreadcrumb', () => { +import { + useBreadcrumbs, + useEnterpriseSearchBreadcrumbs, + useAppSearchBreadcrumbs, + useWorkplaceSearchBreadcrumbs, +} from './generate_breadcrumbs'; + +describe('useBreadcrumbs', () => { beforeEach(() => { jest.clearAllMocks(); }); - it("creates a breadcrumb object matching EUI's breadcrumb type", () => { - const breadcrumb = generateBreadcrumb({ - text: 'Hello World', - path: '/hello_world', - history: mockHistory, - }); - expect(breadcrumb).toEqual({ - text: 'Hello World', - href: '/enterprise_search/hello_world', - onClick: expect.any(Function), - }); + it('accepts an array of breadcrumbs and to the array correctly injects SPA link navigation props', () => { + const breadcrumb = useBreadcrumbs([ + { + text: 'Hello', + path: '/hello', + }, + { + text: 'World', + path: '/world', + }, + ]); + expect(breadcrumb).toEqual([ + { + text: 'Hello', + href: '/enterprise_search/hello', + onClick: expect.any(Function), + }, + { + text: 'World', + href: '/enterprise_search/world', + onClick: expect.any(Function), + }, + ]); }); it('prevents default navigation and uses React Router history on click', () => { - const breadcrumb = generateBreadcrumb({ text: '', path: '/', history: mockHistory }) as any; + const breadcrumb = useBreadcrumbs([{ text: '', path: '/' }])[0] as any; const event = { preventDefault: jest.fn() }; breadcrumb.onClick(event); - expect(mockHistory.push).toHaveBeenCalled(); + expect(mockKibanaContext.navigateToUrl).toHaveBeenCalled(); + expect(mockHistory.createHref).toHaveBeenCalled(); expect(event.preventDefault).toHaveBeenCalled(); }); it('does not prevent default browser behavior on new tab/window clicks', () => { - const breadcrumb = generateBreadcrumb({ text: '', path: '/', history: mockHistory }) as any; + const breadcrumb = useBreadcrumbs([{ text: '', path: '/' }])[0] as any; (letBrowserHandleEvent as jest.Mock).mockImplementationOnce(() => true); breadcrumb.onClick(); - expect(mockHistory.push).not.toHaveBeenCalled(); + expect(mockKibanaContext.navigateToUrl).not.toHaveBeenCalled(); }); it('does not generate link behavior if path is excluded', () => { - const breadcrumb = generateBreadcrumb({ text: 'Unclickable breadcrumb' }); + const breadcrumb = useBreadcrumbs([{ text: 'Unclickable breadcrumb' }])[0]; expect(breadcrumb.href).toBeUndefined(); expect(breadcrumb.onClick).toBeUndefined(); }); }); -describe('enterpriseSearchBreadcrumbs', () => { - const breadCrumbs = [ - { - text: 'Page 1', - path: '/page1', - }, - { - text: 'Page 2', - path: '/page2', - }, - ]; - +describe('useEnterpriseSearchBreadcrumbs', () => { beforeEach(() => { jest.clearAllMocks(); }); - const subject = () => enterpriseSearchBreadcrumbs(mockHistory)(breadCrumbs); + it('builds a chain of breadcrumbs with Enterprise Search at the root', () => { + const breadcrumbs = [ + { + text: 'Page 1', + path: '/page1', + }, + { + text: 'Page 2', + path: '/page2', + }, + ]; - it('Builds a chain of breadcrumbs with Enterprise Search at the root', () => { - expect(subject()).toEqual([ + expect(useEnterpriseSearchBreadcrumbs(breadcrumbs)).toEqual([ { text: 'Enterprise Search', }, { + text: 'Page 1', href: '/enterprise_search/page1', onClick: expect.any(Function), - text: 'Page 1', }, { + text: 'Page 2', href: '/enterprise_search/page2', onClick: expect.any(Function), - text: 'Page 2', }, ]); }); it('shows just the root if breadcrumbs is empty', () => { - expect(enterpriseSearchBreadcrumbs(mockHistory)()).toEqual([ + expect(useEnterpriseSearchBreadcrumbs()).toEqual([ { text: 'Enterprise Search', }, ]); }); - - describe('links', () => { - const eventMock = { - preventDefault: jest.fn(), - } as any; - - it('has Enterprise Search text first', () => { - expect(subject()[0].onClick).toBeUndefined(); - }); - - it('has a link to page 1 second', () => { - (subject()[1] as any).onClick(eventMock); - expect(mockHistory.push).toHaveBeenCalledWith('/page1'); - }); - - it('has a link to page 2 last', () => { - (subject()[2] as any).onClick(eventMock); - expect(mockHistory.push).toHaveBeenCalledWith('/page2'); - }); - }); }); -describe('appSearchBreadcrumbs', () => { - const breadCrumbs = [ - { - text: 'Page 1', - path: '/page1', - }, - { - text: 'Page 2', - path: '/page2', - }, - ]; - +describe('useAppSearchBreadcrumbs', () => { beforeEach(() => { jest.clearAllMocks(); mockHistory.createHref.mockImplementation( @@ -145,82 +126,55 @@ describe('appSearchBreadcrumbs', () => { ); }); - const subject = () => appSearchBreadcrumbs(mockHistory)(breadCrumbs); - it('Builds a chain of breadcrumbs with Enterprise Search and App Search at the root', () => { - expect(subject()).toEqual([ + const breadcrumbs = [ + { + text: 'Page 1', + path: '/page1', + }, + { + text: 'Page 2', + path: '/page2', + }, + ]; + + expect(useAppSearchBreadcrumbs(breadcrumbs)).toEqual([ { text: 'Enterprise Search', }, { + text: 'App Search', href: '/enterprise_search/app_search/', onClick: expect.any(Function), - text: 'App Search', }, { + text: 'Page 1', href: '/enterprise_search/app_search/page1', onClick: expect.any(Function), - text: 'Page 1', }, { + text: 'Page 2', href: '/enterprise_search/app_search/page2', onClick: expect.any(Function), - text: 'Page 2', }, ]); }); it('shows just the root if breadcrumbs is empty', () => { - expect(appSearchBreadcrumbs(mockHistory)()).toEqual([ + expect(useAppSearchBreadcrumbs()).toEqual([ { text: 'Enterprise Search', }, { + text: 'App Search', href: '/enterprise_search/app_search/', onClick: expect.any(Function), - text: 'App Search', }, ]); }); - - describe('links', () => { - const eventMock = { - preventDefault: jest.fn(), - } as any; - - it('has Enterprise Search text first', () => { - expect(subject()[0].onClick).toBeUndefined(); - }); - - it('has a link to App Search second', () => { - (subject()[1] as any).onClick(eventMock); - expect(mockHistory.push).toHaveBeenCalledWith('/'); - }); - - it('has a link to page 1 third', () => { - (subject()[2] as any).onClick(eventMock); - expect(mockHistory.push).toHaveBeenCalledWith('/page1'); - }); - - it('has a link to page 2 last', () => { - (subject()[3] as any).onClick(eventMock); - expect(mockHistory.push).toHaveBeenCalledWith('/page2'); - }); - }); }); -describe('workplaceSearchBreadcrumbs', () => { - const breadCrumbs = [ - { - text: 'Page 1', - path: '/page1', - }, - { - text: 'Page 2', - path: '/page2', - }, - ]; - +describe('useWorkplaceSearchBreadcrumbs', () => { beforeEach(() => { jest.clearAllMocks(); mockHistory.createHref.mockImplementation( @@ -228,66 +182,50 @@ describe('workplaceSearchBreadcrumbs', () => { ); }); - const subject = () => workplaceSearchBreadcrumbs(mockHistory)(breadCrumbs); - it('Builds a chain of breadcrumbs with Enterprise Search and Workplace Search at the root', () => { - expect(subject()).toEqual([ + const breadcrumbs = [ + { + text: 'Page 1', + path: '/page1', + }, + { + text: 'Page 2', + path: '/page2', + }, + ]; + + expect(useWorkplaceSearchBreadcrumbs(breadcrumbs)).toEqual([ { text: 'Enterprise Search', }, { + text: 'Workplace Search', href: '/enterprise_search/workplace_search/', onClick: expect.any(Function), - text: 'Workplace Search', }, { + text: 'Page 1', href: '/enterprise_search/workplace_search/page1', onClick: expect.any(Function), - text: 'Page 1', }, { + text: 'Page 2', href: '/enterprise_search/workplace_search/page2', onClick: expect.any(Function), - text: 'Page 2', }, ]); }); it('shows just the root if breadcrumbs is empty', () => { - expect(workplaceSearchBreadcrumbs(mockHistory)()).toEqual([ + expect(useWorkplaceSearchBreadcrumbs()).toEqual([ { text: 'Enterprise Search', }, { + text: 'Workplace Search', href: '/enterprise_search/workplace_search/', onClick: expect.any(Function), - text: 'Workplace Search', }, ]); }); - - describe('links', () => { - const eventMock = { - preventDefault: jest.fn(), - } as any; - - it('has Enterprise Search text first', () => { - expect(subject()[0].onClick).toBeUndefined(); - }); - - it('has a link to Workplace Search second', () => { - (subject()[1] as any).onClick(eventMock); - expect(mockHistory.push).toHaveBeenCalledWith('/'); - }); - - it('has a link to page 1 third', () => { - (subject()[2] as any).onClick(eventMock); - expect(mockHistory.push).toHaveBeenCalledWith('/page1'); - }); - - it('has a link to page 2 last', () => { - (subject()[3] as any).onClick(eventMock); - expect(mockHistory.push).toHaveBeenCalledWith('/page2'); - }); - }); }); 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 82c0f78fb853f..6eab936719d01 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 @@ -4,8 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ +import { useContext } from 'react'; +import { useHistory } from 'react-router-dom'; import { EuiBreadcrumb } from '@elastic/eui'; -import { History } from 'history'; + +import { KibanaContext, IKibanaContext } from '../../index'; import { ENTERPRISE_SEARCH_PLUGIN, @@ -20,50 +23,46 @@ import { letBrowserHandleEvent } from '../react_router_helpers'; * https://elastic.github.io/eui/#/navigation/breadcrumbs */ -interface IGenerateBreadcrumbProps { +interface IBreadcrumb { text: string; path?: string; - history?: History; } +export type TBreadcrumbs = IBreadcrumb[]; + +export const useBreadcrumbs = (breadcrumbs: TBreadcrumbs) => { + const history = useHistory(); + const { navigateToUrl } = useContext(KibanaContext) as IKibanaContext; + + return breadcrumbs.map(({ text, path }) => { + const breadcrumb = { text } as EuiBreadcrumb; -export const generateBreadcrumb = ({ text, path, history }: IGenerateBreadcrumbProps) => { - const breadcrumb = { text } as EuiBreadcrumb; + if (path) { + const href = history.createHref({ pathname: path }) as string; - if (path && history) { - breadcrumb.href = history.createHref({ pathname: path }); - breadcrumb.onClick = (event) => { - if (letBrowserHandleEvent(event)) return; - event.preventDefault(); - history.push(path); - }; - } + breadcrumb.href = href; + breadcrumb.onClick = (event) => { + if (letBrowserHandleEvent(event)) return; + event.preventDefault(); + navigateToUrl(href); + }; + } - return breadcrumb; + return breadcrumb; + }); }; /** * Product-specific breadcrumb helpers */ -export type TBreadcrumbs = IGenerateBreadcrumbProps[]; +export const useEnterpriseSearchBreadcrumbs = (breadcrumbs: TBreadcrumbs = []) => + useBreadcrumbs([{ text: ENTERPRISE_SEARCH_PLUGIN.NAME }, ...breadcrumbs]); -export const enterpriseSearchBreadcrumbs = (history: History) => ( - breadcrumbs: TBreadcrumbs = [] -) => [ - generateBreadcrumb({ text: ENTERPRISE_SEARCH_PLUGIN.NAME }), - ...breadcrumbs.map(({ text, path }: IGenerateBreadcrumbProps) => - generateBreadcrumb({ text, path, history }) - ), -]; - -export const appSearchBreadcrumbs = (history: History) => (breadcrumbs: TBreadcrumbs = []) => - enterpriseSearchBreadcrumbs(history)([ - { text: APP_SEARCH_PLUGIN.NAME, path: '/' }, - ...breadcrumbs, - ]); +export const useAppSearchBreadcrumbs = (breadcrumbs: TBreadcrumbs = []) => + useEnterpriseSearchBreadcrumbs([{ text: APP_SEARCH_PLUGIN.NAME, path: '/' }, ...breadcrumbs]); -export const workplaceSearchBreadcrumbs = (history: History) => (breadcrumbs: TBreadcrumbs = []) => - enterpriseSearchBreadcrumbs(history)([ +export const useWorkplaceSearchBreadcrumbs = (breadcrumbs: TBreadcrumbs = []) => + useEnterpriseSearchBreadcrumbs([ { text: WORKPLACE_SEARCH_PLUGIN.NAME, path: '/' }, ...breadcrumbs, ]); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.test.tsx index aba0b250e56c0..bda816c9a5554 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.test.tsx @@ -4,16 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ +import '../../__mocks__/shallow_usecontext.mock'; +import '../../__mocks__/react_router_history.mock'; + import React from 'react'; -import '../../__mocks__/react_router_history.mock'; import { mockKibanaContext, mountWithKibanaContext } from '../../__mocks__'; jest.mock('./generate_breadcrumbs', () => ({ - appSearchBreadcrumbs: jest.fn(() => (crumbs: any) => crumbs), - workplaceSearchBreadcrumbs: jest.fn(() => (crumbs: any) => crumbs), + useAppSearchBreadcrumbs: jest.fn(() => (crumbs: any) => crumbs), + useWorkplaceSearchBreadcrumbs: jest.fn(() => (crumbs: any) => crumbs), })); -import { appSearchBreadcrumbs, workplaceSearchBreadcrumbs } from './generate_breadcrumbs'; +import { useAppSearchBreadcrumbs, useWorkplaceSearchBreadcrumbs } from './generate_breadcrumbs'; jest.mock('./generate_title', () => ({ appSearchTitle: jest.fn((title: any) => title), @@ -23,62 +25,55 @@ import { appSearchTitle, workplaceSearchTitle } from './generate_title'; import { SetAppSearchChrome, SetWorkplaceSearchChrome } from './'; -describe('SetAppSearchChrome', () => { +describe('Set Kibana Chrome helpers', () => { beforeEach(() => { jest.clearAllMocks(); }); afterEach(() => { - expect(appSearchBreadcrumbs).toHaveBeenCalled(); - expect(appSearchTitle).toHaveBeenCalled(); - }); - - it('sets breadcrumbs and document title', () => { - mountWithKibanaContext(); - - expect(mockKibanaContext.setBreadcrumbs).toHaveBeenCalledWith([ - { - text: 'Engines', - path: '/current-path', - }, - ]); - expect(mockKibanaContext.setDocTitle).toHaveBeenCalledWith(['Engines']); + expect(mockKibanaContext.setBreadcrumbs).toHaveBeenCalled(); + expect(mockKibanaContext.setDocTitle).toHaveBeenCalled(); }); - it('sets empty breadcrumbs and document title when isRoot is true', () => { - mountWithKibanaContext(); - - expect(mockKibanaContext.setBreadcrumbs).toHaveBeenCalledWith([]); - expect(mockKibanaContext.setDocTitle).toHaveBeenCalledWith([]); + describe('SetAppSearchChrome', () => { + it('sets breadcrumbs and document title', () => { + mountWithKibanaContext(); + + expect(appSearchTitle).toHaveBeenCalledWith(['Engines']); + expect(useAppSearchBreadcrumbs).toHaveBeenCalledWith([ + { + text: 'Engines', + path: '/current-path', + }, + ]); + }); + + it('sets empty breadcrumbs and document title when isRoot is true', () => { + mountWithKibanaContext(); + + expect(appSearchTitle).toHaveBeenCalledWith([]); + expect(useAppSearchBreadcrumbs).toHaveBeenCalledWith([]); + }); }); -}); - -describe('SetWorkplaceSearchChrome', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - afterEach(() => { - expect(workplaceSearchBreadcrumbs).toHaveBeenCalled(); - expect(workplaceSearchTitle).toHaveBeenCalled(); - }); - - it('sets breadcrumbs and document title', () => { - mountWithKibanaContext(); - - expect(mockKibanaContext.setBreadcrumbs).toHaveBeenCalledWith([ - { - text: 'Sources', - path: '/current-path', - }, - ]); - expect(mockKibanaContext.setDocTitle).toHaveBeenCalledWith(['Sources']); - }); - - it('sets empty breadcrumbs and document title when isRoot is true', () => { - mountWithKibanaContext(); - expect(mockKibanaContext.setBreadcrumbs).toHaveBeenCalledWith([]); - expect(mockKibanaContext.setDocTitle).toHaveBeenCalledWith([]); + describe('SetWorkplaceSearchChrome', () => { + it('sets breadcrumbs and document title', () => { + mountWithKibanaContext(); + + expect(workplaceSearchTitle).toHaveBeenCalledWith(['Sources']); + expect(useWorkplaceSearchBreadcrumbs).toHaveBeenCalledWith([ + { + text: 'Sources', + path: '/current-path', + }, + ]); + }); + + it('sets empty breadcrumbs and document title when isRoot is true', () => { + mountWithKibanaContext(); + + expect(workplaceSearchTitle).toHaveBeenCalledWith([]); + expect(useWorkplaceSearchBreadcrumbs).toHaveBeenCalledWith([]); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx index 59e83a2cb13c2..43db93c1583d1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx @@ -7,10 +7,11 @@ import React, { useContext, useEffect } from 'react'; import { useHistory } from 'react-router-dom'; import { EuiBreadcrumb } from '@elastic/eui'; + import { KibanaContext, IKibanaContext } from '../../index'; import { - appSearchBreadcrumbs, - workplaceSearchBreadcrumbs, + useAppSearchBreadcrumbs, + useWorkplaceSearchBreadcrumbs, TBreadcrumbs, } from './generate_breadcrumbs'; import { appSearchTitle, workplaceSearchTitle, TTitle } from './generate_title'; @@ -36,12 +37,15 @@ export const SetAppSearchChrome: React.FC = ({ text, isRoot } const history = useHistory(); const { setBreadcrumbs, setDocTitle } = useContext(KibanaContext) as IKibanaContext; - const crumb = isRoot ? [] : [{ text, path: history.location.pathname }]; const title = isRoot ? [] : [text]; + const docTitle = appSearchTitle(title as TTitle | []); + + const crumb = isRoot ? [] : [{ text, path: history.location.pathname }]; + const breadcrumbs = useAppSearchBreadcrumbs(crumb as TBreadcrumbs | []); useEffect(() => { - setBreadcrumbs(appSearchBreadcrumbs(history)(crumb as TBreadcrumbs | [])); - setDocTitle(appSearchTitle(title as TTitle | [])); + setBreadcrumbs(breadcrumbs); + setDocTitle(docTitle); }, []); return null; @@ -51,12 +55,15 @@ export const SetWorkplaceSearchChrome: React.FC = ({ text, is const history = useHistory(); const { setBreadcrumbs, setDocTitle } = useContext(KibanaContext) as IKibanaContext; - const crumb = isRoot ? [] : [{ text, path: history.location.pathname }]; const title = isRoot ? [] : [text]; + const docTitle = workplaceSearchTitle(title as TTitle | []); + + const crumb = isRoot ? [] : [{ text, path: history.location.pathname }]; + const breadcrumbs = useWorkplaceSearchBreadcrumbs(crumb as TBreadcrumbs | []); useEffect(() => { - setBreadcrumbs(workplaceSearchBreadcrumbs(history)(crumb as TBreadcrumbs | [])); - setDocTitle(workplaceSearchTitle(title as TTitle | [])); + setBreadcrumbs(breadcrumbs); + setDocTitle(docTitle); }, []); return null; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx index 76ee8293f2c8b..063118f94cd19 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx @@ -4,12 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ +import '../../__mocks__/shallow_usecontext.mock'; +import '../../__mocks__/react_router_history.mock'; + import React from 'react'; import { shallow, mount } from 'enzyme'; import { EuiLink, EuiButton } from '@elastic/eui'; -import '../../__mocks__/react_router_history.mock'; -import { mockHistory } from '../../__mocks__'; +import { mockKibanaContext, mockHistory } from '../../__mocks__'; import { EuiReactRouterLink, EuiReactRouterButton } from './eui_link'; @@ -59,7 +61,7 @@ describe('EUI & React Router Component Helpers', () => { wrapper.find(EuiLink).simulate('click', simulatedEvent); expect(simulatedEvent.preventDefault).toHaveBeenCalled(); - expect(mockHistory.push).toHaveBeenCalled(); + expect(mockKibanaContext.navigateToUrl).toHaveBeenCalled(); }); it('does not prevent default browser behavior on new tab/window clicks', () => { @@ -71,7 +73,7 @@ describe('EUI & React Router Component Helpers', () => { }; wrapper.find(EuiLink).simulate('click', simulatedEvent); - expect(mockHistory.push).not.toHaveBeenCalled(); + expect(mockKibanaContext.navigateToUrl).not.toHaveBeenCalled(); }); it('calls inherited onClick actions in addition to default navigation', () => { 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 b53b2f2b3b650..7221a61d0997b 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 @@ -4,10 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useContext } from 'react'; import { useHistory } from 'react-router-dom'; import { EuiLink, EuiButton, EuiButtonProps, EuiLinkAnchorProps } from '@elastic/eui'; +import { KibanaContext, IKibanaContext } from '../../index'; import { letBrowserHandleEvent } from './link_events'; /** @@ -24,6 +25,10 @@ interface IEuiReactRouterProps { export const EuiReactRouterHelper: React.FC = ({ to, onClick, children }) => { const history = useHistory(); + const { navigateToUrl } = useContext(KibanaContext) as IKibanaContext; + + // Generate the correct link href (with basename etc. accounted for) + const href = history.createHref({ pathname: to }); const reactRouterLinkClick = (event: React.MouseEvent) => { if (onClick) onClick(); // Run any passed click events (e.g. telemetry) @@ -32,13 +37,10 @@ export const EuiReactRouterHelper: React.FC = ({ to, onCli // Prevent regular link behavior, which causes a browser refresh. event.preventDefault(); - // Push the route to the history. - history.push(to); + // Perform SPA navigation. + navigateToUrl(href); }; - // Generate the correct link href (with basename etc. accounted for) - const href = history.createHref({ pathname: to }); - const reactRouterProps = { href, onClick: reactRouterLinkClick }; return React.cloneElement(children as React.ReactElement, reactRouterProps); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx index ca0d395c0d673..4aa171a5a5762 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx @@ -33,7 +33,6 @@ export const WorkplaceSearch: React.FC = () => { - {/* Kibana displays a blank page on redirect if this isn't included */} );