diff --git a/src/core/public/chrome/chrome_service.test.ts b/src/core/public/chrome/chrome_service.test.ts index 5840f826ba12..caaa588c2c9f 100644 --- a/src/core/public/chrome/chrome_service.test.ts +++ b/src/core/public/chrome/chrome_service.test.ts @@ -202,19 +202,62 @@ Array [ "baz", ], ] +`); + }); + }); + + describe('breadcrumbs', () => { + it('updates/emits the current set of breadcrumbs', async () => { + const service = new ChromeService(); + const start = service.start(); + const promise = start + .getBreadcrumbs$() + .pipe(toArray()) + .toPromise(); + + start.setBreadcrumbs([{ text: 'foo' }, { text: 'bar' }]); + start.setBreadcrumbs([{ text: 'foo' }]); + start.setBreadcrumbs([{ text: 'bar' }]); + start.setBreadcrumbs([]); + service.stop(); + + await expect(promise).resolves.toMatchInlineSnapshot(` +Array [ + Array [], + Array [ + Object { + "text": "foo", + }, + Object { + "text": "bar", + }, + ], + Array [ + Object { + "text": "foo", + }, + ], + Array [ + Object { + "text": "bar", + }, + ], + Array [], +] `); }); }); }); describe('stop', () => { - it('completes applicationClass$, isCollapsed$, isVisible$, and brand$ observables', async () => { + it('completes applicationClass$, isCollapsed$, breadcrumbs$, isVisible$, and brand$ observables', async () => { const service = new ChromeService(); const start = service.start(); const promise = Rx.combineLatest( start.getBrand$(), start.getApplicationClasses$(), start.getIsCollapsed$(), + start.getBreadcrumbs$(), start.getIsVisible$() ).toPromise(); @@ -232,6 +275,7 @@ describe('stop', () => { start.getBrand$(), start.getApplicationClasses$(), start.getIsCollapsed$(), + start.getBreadcrumbs$(), start.getIsVisible$() ).toPromise() ).resolves.toBe(undefined); diff --git a/src/core/public/chrome/chrome_service.ts b/src/core/public/chrome/chrome_service.ts index 1e634aa42e2d..8695385c9d20 100644 --- a/src/core/public/chrome/chrome_service.ts +++ b/src/core/public/chrome/chrome_service.ts @@ -34,6 +34,11 @@ export interface Brand { smallLogo?: string; } +export interface Breadcrumb { + text: string; + href?: string; +} + export class ChromeService { private readonly stop$ = new Rx.ReplaySubject(1); @@ -44,6 +49,7 @@ export class ChromeService { const isVisible$ = new Rx.BehaviorSubject(true); const isCollapsed$ = new Rx.BehaviorSubject(!!localStorage.getItem(IS_COLLAPSED_KEY)); const applicationClasses$ = new Rx.BehaviorSubject>(new Set()); + const breadcrumbs$ = new Rx.BehaviorSubject([]); return { /** @@ -135,6 +141,18 @@ export class ChromeService { map(set => [...set]), takeUntil(this.stop$) ), + + /** + * Get an observable of the current list of breadcrumbs + */ + getBreadcrumbs$: () => breadcrumbs$.pipe(takeUntil(this.stop$)), + + /** + * Override the current set of breadcrumbs + */ + setBreadcrumbs: (newBreadcrumbs: Breadcrumb[]) => { + breadcrumbs$.next(newBreadcrumbs); + }, }; } diff --git a/src/core/public/chrome/index.ts b/src/core/public/chrome/index.ts index afc3d237ecec..ac54469e20bd 100644 --- a/src/core/public/chrome/index.ts +++ b/src/core/public/chrome/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { ChromeService, ChromeStartContract, Brand } from './chrome_service'; +export { Breadcrumb, ChromeService, ChromeStartContract, Brand } from './chrome_service'; diff --git a/src/core/public/legacy_platform/__snapshots__/legacy_platform_service.test.ts.snap b/src/core/public/legacy_platform/__snapshots__/legacy_platform_service.test.ts.snap index b4a3fb8eface..a1474127605d 100644 --- a/src/core/public/legacy_platform/__snapshots__/legacy_platform_service.test.ts.snap +++ b/src/core/public/legacy_platform/__snapshots__/legacy_platform_service.test.ts.snap @@ -11,6 +11,7 @@ Array [ "ui/chrome/api/injected_vars", "ui/chrome/api/controls", "ui/chrome/api/theme", + "ui/chrome/api/breadcrumbs", "ui/chrome/services/global_nav_state", "ui/chrome", "legacy files", @@ -28,6 +29,7 @@ Array [ "ui/chrome/api/injected_vars", "ui/chrome/api/controls", "ui/chrome/api/theme", + "ui/chrome/api/breadcrumbs", "ui/chrome/services/global_nav_state", "ui/test_harness", "legacy files", diff --git a/src/core/public/legacy_platform/legacy_platform_service.test.ts b/src/core/public/legacy_platform/legacy_platform_service.test.ts index 8abf529a2a6b..957731fb7bcb 100644 --- a/src/core/public/legacy_platform/legacy_platform_service.test.ts +++ b/src/core/public/legacy_platform/legacy_platform_service.test.ts @@ -110,6 +110,14 @@ jest.mock('ui/chrome/api/theme', () => { }; }); +const mockChromeBreadcrumbsInit = jest.fn(); +jest.mock('ui/chrome/api/breadcrumbs', () => { + mockLoadOrder.push('ui/chrome/api/breadcrumbs'); + return { + __newPlatformInit__: mockChromeBreadcrumbsInit, + }; +}); + const mockGlobalNavStateInit = jest.fn(); jest.mock('ui/chrome/services/global_nav_state', () => { mockLoadOrder.push('ui/chrome/services/global_nav_state'); @@ -272,6 +280,17 @@ describe('#start()', () => { expect(mockChromeThemeInit).toHaveBeenCalledWith(chromeStartContract); }); + it('passes chrome service to ui/chrome/api/breadcrumbs', () => { + const legacyPlatform = new LegacyPlatformService({ + ...defaultParams, + }); + + legacyPlatform.start(defaultStartDeps); + + expect(mockChromeBreadcrumbsInit).toHaveBeenCalledTimes(1); + expect(mockChromeBreadcrumbsInit).toHaveBeenCalledWith(chromeStartContract); + }); + it('passes chrome service to ui/chrome/api/global_nav_state', () => { const legacyPlatform = new LegacyPlatformService({ ...defaultParams, diff --git a/src/core/public/legacy_platform/legacy_platform_service.ts b/src/core/public/legacy_platform/legacy_platform_service.ts index 8354b9592f84..54bb912614cb 100644 --- a/src/core/public/legacy_platform/legacy_platform_service.ts +++ b/src/core/public/legacy_platform/legacy_platform_service.ts @@ -72,6 +72,7 @@ export class LegacyPlatformService { require('ui/chrome/api/injected_vars').__newPlatformInit__(injectedMetadata); require('ui/chrome/api/controls').__newPlatformInit__(chrome); require('ui/chrome/api/theme').__newPlatformInit__(chrome); + require('ui/chrome/api/breadcrumbs').__newPlatformInit__(chrome); require('ui/chrome/services/global_nav_state').__newPlatformInit__(chrome); // Load the bootstrap module before loading the legacy platform files so that diff --git a/src/core_plugins/kibana/public/dashboard/dashboard_app.js b/src/core_plugins/kibana/public/dashboard/dashboard_app.js index 3a1fd54a24de..919191885a43 100644 --- a/src/core_plugins/kibana/public/dashboard/dashboard_app.js +++ b/src/core_plugins/kibana/public/dashboard/dashboard_app.js @@ -85,11 +85,9 @@ app.directive('dashboardApp', function ($injector) { $rootScope, $route, $routeParams, - $location, getAppState, dashboardConfig, localStorage, - breadcrumbState, i18n, ) { const filterManager = Private(FilterManagerProvider); @@ -182,7 +180,7 @@ app.directive('dashboardApp', function ($injector) { // Push breadcrumbs to new header navigation const updateBreadcrumbs = () => { - breadcrumbState.set([ + chrome.breadcrumbs.set([ { text: i18n('kbn.dashboard.dashboardAppBreadcrumbsTitle', { defaultMessage: 'Dashboard', diff --git a/src/core_plugins/kibana/public/dashboard/index.js b/src/core_plugins/kibana/public/dashboard/index.js index a2965219d68b..72280b7bc9d7 100644 --- a/src/core_plugins/kibana/public/dashboard/index.js +++ b/src/core_plugins/kibana/public/dashboard/index.js @@ -22,6 +22,7 @@ import './dashboard_app'; import './saved_dashboard/saved_dashboards'; import './dashboard_config'; import uiRoutes from 'ui/routes'; +import chrome from 'ui/chrome'; import { toastNotifications } from 'ui/notify'; import dashboardTemplate from './dashboard_app.html'; @@ -57,7 +58,7 @@ uiRoutes }) .when(DashboardConstants.LANDING_PAGE_PATH, { template: dashboardListingTemplate, - controller($injector, $location, $scope, Private, config, breadcrumbState, i18n) { + controller($injector, $location, $scope, Private, config, i18n) { const services = Private(SavedObjectRegistryProvider).byLoaderPropertiesName; const dashboardConfig = $injector.get('dashboardConfig'); @@ -70,7 +71,7 @@ uiRoutes }; $scope.hideWriteControls = dashboardConfig.getHideWriteControls(); $scope.initialFilter = ($location.search()).filter || EMPTY_FILTER; - breadcrumbState.set([{ + chrome.breadcrumbs.set([{ text: i18n('kbn.dashboard.dashboardBreadcrumbsTitle', { defaultMessage: 'Dashboards', }), diff --git a/src/core_plugins/kibana/public/discover/controllers/discover.js b/src/core_plugins/kibana/public/discover/controllers/discover.js index 03acb19a18af..a10ef78da11c 100644 --- a/src/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/core_plugins/kibana/public/discover/controllers/discover.js @@ -20,6 +20,7 @@ import _ from 'lodash'; import React from 'react'; import angular from 'angular'; +import chrome from 'ui/chrome'; import { getSort } from 'ui/doc_table/lib/get_sort'; import * as columnActions from 'ui/doc_table/actions/columns'; import * as filterActions from 'ui/doc_table/actions/filter'; @@ -155,7 +156,6 @@ function discoverController( courier, kbnUrl, localStorage, - breadcrumbState, i18n, ) { const Vis = Private(VisProvider); @@ -320,12 +320,12 @@ function discoverController( }); if (savedSearch.id && savedSearch.title) { - breadcrumbState.set([{ + chrome.breadcrumbs.set([{ text: discoverBreadcrumbsTitle, href: '#/discover' }, { text: savedSearch.title }]); } else { - breadcrumbState.set([{ + chrome.breadcrumbs.set([{ text: discoverBreadcrumbsTitle, }]); } diff --git a/src/core_plugins/kibana/public/visualize/listing/visualize_listing.js b/src/core_plugins/kibana/public/visualize/listing/visualize_listing.js index 997023064f6b..519ed05cc499 100644 --- a/src/core_plugins/kibana/public/visualize/listing/visualize_listing.js +++ b/src/core_plugins/kibana/public/visualize/listing/visualize_listing.js @@ -23,6 +23,7 @@ import 'ui/pager'; import { uiModules } from 'ui/modules'; import { timefilter } from 'ui/timefilter'; import { i18n } from '@kbn/i18n'; +import chrome from 'ui/chrome'; import { VisualizeListingTable } from './visualize_listing_table'; @@ -35,7 +36,6 @@ export function VisualizeListingController($injector) { const Notifier = $injector.get('Notifier'); const Private = $injector.get('Private'); const config = $injector.get('config'); - const breadcrumbState = $injector.get('breadcrumbState'); timefilter.disableAutoRefreshSelector(); timefilter.disableTimeRangeSelector(); @@ -61,7 +61,7 @@ export function VisualizeListingController($injector) { .catch(error => notify.error(error)); }; - breadcrumbState.set([{ + chrome.breadcrumbs.set([{ text: i18n.translate('kbn.visualize.visualizeListingBreadcrumbsTitle', { defaultMessage: 'Visualize', }) diff --git a/src/ui/public/chrome/api/angular.js b/src/ui/public/chrome/api/angular.js index 19fcfa410f24..c9ed0153fe97 100644 --- a/src/ui/public/chrome/api/angular.js +++ b/src/ui/public/chrome/api/angular.js @@ -67,6 +67,7 @@ export function initAngularApi(chrome, internals) { $locationProvider.hashPrefix(''); }) .run(internals.capture$httpLoadingCount) + .run(internals.$setupBreadcrumbsAutoClear) .run(($location, $rootScope, Private, config) => { chrome.getFirstPathSegment = () => { return $location.path().split('/')[1]; diff --git a/src/ui/public/chrome/api/breadcrumbs.ts b/src/ui/public/chrome/api/breadcrumbs.ts new file mode 100644 index 000000000000..83c29a2bcb77 --- /dev/null +++ b/src/ui/public/chrome/api/breadcrumbs.ts @@ -0,0 +1,75 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// @ts-ignore +import { uiModules } from 'ui/modules'; +import { Breadcrumb, ChromeStartContract } from '../../../../core/public/chrome'; +export { Breadcrumb }; + +let newPlatformChrome: ChromeStartContract; +export function __newPlatformInit__(instance: ChromeStartContract) { + if (newPlatformChrome) { + throw new Error('ui/chrome/api/breadcrumbs is already initialized'); + } + + newPlatformChrome = instance; +} + +export function initBreadcrumbsApi( + chrome: { [key: string]: any }, + internals: { [key: string]: any } +) { + // A flag used to determine if we should automatically + // clear the breadcrumbs between angular route changes. + let shouldClear = false; + + // reset shouldClear any time the breadcrumbs change, even + // if it was done directly through the new platform + newPlatformChrome.getBreadcrumbs$().subscribe({ + next() { + shouldClear = false; + }, + }); + + chrome.breadcrumbs = { + get$() { + return newPlatformChrome.getBreadcrumbs$(); + }, + + set(newBreadcrumbs: Breadcrumb[]) { + newPlatformChrome.setBreadcrumbs(newBreadcrumbs); + }, + }; + + // define internal angular run function that will be called when angular + // bootstraps and lets us integrate with the angular router so that we can + // automatically clear the breadcrumbs if we switch to a Kibana app that + // does not use breadcrumbs correctly + internals.$setupBreadcrumbsAutoClear = ($rootScope: any) => { + $rootScope.$on('$routeChangeStart', () => { + shouldClear = true; + }); + + $rootScope.$on('$routeChangeSuccess', () => { + if (shouldClear) { + newPlatformChrome.setBreadcrumbs([]); + } + }); + }; +} diff --git a/src/ui/public/chrome/chrome.js b/src/ui/public/chrome/chrome.js index ffd3e5da2594..7eba95107680 100644 --- a/src/ui/public/chrome/chrome.js +++ b/src/ui/public/chrome/chrome.js @@ -35,6 +35,7 @@ import { initAngularApi } from './api/angular'; import appsApi from './api/apps'; import { initChromeControlsApi } from './api/controls'; import { initChromeNavApi } from './api/nav'; +import { initBreadcrumbsApi } from './api/breadcrumbs'; import templateApi from './api/template'; import { initChromeThemeApi } from './api/theme'; import { initChromeXsrfApi } from './api/xsrf'; @@ -67,6 +68,7 @@ initChromeXsrfApi(chrome, internals); initChromeBasePathApi(chrome); initChromeInjectedVarsApi(chrome); initChromeNavApi(chrome, internals); +initBreadcrumbsApi(chrome, internals); initLoadingCountApi(chrome, internals); initAngularApi(chrome, internals); initChromeControlsApi(chrome); diff --git a/src/ui/public/chrome/directives/header_global_nav/components/__snapshots__/header_breadcrumbs.test.tsx.snap b/src/ui/public/chrome/directives/header_global_nav/components/__snapshots__/header_breadcrumbs.test.tsx.snap index 74243a506a0f..4b97c565f3d4 100644 --- a/src/ui/public/chrome/directives/header_global_nav/components/__snapshots__/header_breadcrumbs.test.tsx.snap +++ b/src/ui/public/chrome/directives/header_global_nav/components/__snapshots__/header_breadcrumbs.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`HeaderBreadcrumbs renders updates to the breadcrumbs observable 1`] = ` +exports[`HeaderBreadcrumbs renders updates to the breadcrumbs$ observable 1`] = ` `; -exports[`HeaderBreadcrumbs renders updates to the breadcrumbs observable 2`] = ` +exports[`HeaderBreadcrumbs renders updates to the breadcrumbs$ observable 2`] = ` Array [ ; + breadcrumbs$: Rx.Observable; homeHref: string; isVisible: boolean; navLinks: NavLink[]; @@ -66,7 +67,7 @@ class HeaderUI extends Component { } public render() { - const { appTitle, breadcrumbs, isVisible, navControls, navLinks } = this.props; + const { appTitle, breadcrumbs$, isVisible, navControls, navLinks } = this.props; if (!isVisible) { return null; @@ -82,7 +83,7 @@ class HeaderUI extends Component { - + diff --git a/src/ui/public/chrome/directives/header_global_nav/components/header_breadcrumbs.test.tsx b/src/ui/public/chrome/directives/header_global_nav/components/header_breadcrumbs.test.tsx index 73c3724b1a95..dabf36f91317 100644 --- a/src/ui/public/chrome/directives/header_global_nav/components/header_breadcrumbs.test.tsx +++ b/src/ui/public/chrome/directives/header_global_nav/components/header_breadcrumbs.test.tsx @@ -19,23 +19,25 @@ import { mount } from 'enzyme'; import React from 'react'; -import { breadcrumbs, set } from '../../../services/breadcrumb_state'; +import * as Rx from 'rxjs'; +import { Breadcrumb } from '../../../../../../core/public/chrome'; import { HeaderBreadcrumbs } from './header_breadcrumbs'; describe('HeaderBreadcrumbs', () => { - it('renders updates to the breadcrumbs observable', () => { - const wrapper = mount(); + it('renders updates to the breadcrumbs$ observable', () => { + const breadcrumbs$ = new Rx.Subject(); + const wrapper = mount(); - set([{ text: 'First' }]); + breadcrumbs$.next([{ text: 'First' }]); // Unfortunately, enzyme won't update the wrapper until we call update. wrapper.update(); expect(wrapper.find('.euiBreadcrumb')).toMatchSnapshot(); - set([{ text: 'First' }, { text: 'Second' }]); + breadcrumbs$.next([{ text: 'First' }, { text: 'Second' }]); wrapper.update(); expect(wrapper.find('.euiBreadcrumb')).toMatchSnapshot(); - set([]); + breadcrumbs$.next([]); wrapper.update(); expect(wrapper.find('.euiBreadcrumb')).toMatchSnapshot(); }); diff --git a/src/ui/public/chrome/directives/header_global_nav/components/header_breadcrumbs.tsx b/src/ui/public/chrome/directives/header_global_nav/components/header_breadcrumbs.tsx index ff880ccbd9f6..828de8bf7d31 100644 --- a/src/ui/public/chrome/directives/header_global_nav/components/header_breadcrumbs.tsx +++ b/src/ui/public/chrome/directives/header_global_nav/components/header_breadcrumbs.tsx @@ -18,18 +18,18 @@ */ import React, { Component } from 'react'; -import { Subscribable, Unsubscribable } from 'rxjs'; +import * as Rx from 'rxjs'; import { // @ts-ignore EuiHeaderBreadcrumbs, } from '@elastic/eui'; -import { Breadcrumb } from '../'; +import { Breadcrumb } from '../../../../../../core/public/chrome'; interface Props { appTitle?: string; - breadcrumbs: Subscribable; + breadcrumbs$: Rx.Observable; } interface State { @@ -37,7 +37,7 @@ interface State { } export class HeaderBreadcrumbs extends Component { - private unsubscribable?: Unsubscribable; + private subscription?: Rx.Subscription; constructor(props: Props) { super(props); @@ -50,7 +50,7 @@ export class HeaderBreadcrumbs extends Component { } public componentDidUpdate(prevProps: Props) { - if (prevProps.breadcrumbs === this.props.breadcrumbs) { + if (prevProps.breadcrumbs$ === this.props.breadcrumbs$) { return; } @@ -73,15 +73,17 @@ export class HeaderBreadcrumbs extends Component { } private subscribe() { - this.unsubscribable = this.props.breadcrumbs.subscribe(breadcrumbs => { - this.setState({ breadcrumbs }); + this.subscription = this.props.breadcrumbs$.subscribe(breadcrumbs => { + this.setState({ + breadcrumbs, + }); }); } private unsubscribe() { - if (this.unsubscribable) { - this.unsubscribable.unsubscribe(); - delete this.unsubscribable; + if (this.subscription) { + this.subscription.unsubscribe(); + delete this.subscription; } } } diff --git a/src/ui/public/chrome/directives/header_global_nav/header_global_nav.js b/src/ui/public/chrome/directives/header_global_nav/header_global_nav.js index 1d6048b3f6b7..10773e22894f 100644 --- a/src/ui/public/chrome/directives/header_global_nav/header_global_nav.js +++ b/src/ui/public/chrome/directives/header_global_nav/header_global_nav.js @@ -22,7 +22,6 @@ import { uiModules } from '../../../modules'; import { Header } from './components/header'; import './header_global_nav.less'; import { chromeHeaderNavControlsRegistry } from 'ui/registry/chrome_header_nav_controls'; -import { breadcrumbs } from '../../services/breadcrumb_state'; import { injectI18nProvider } from '@kbn/i18n/react'; const module = uiModules.get('kibana'); @@ -40,7 +39,7 @@ module.directive('headerGlobalNav', (reactDirective, chrome, Private) => { {}, // angular injected React props { - breadcrumbs, + breadcrumbs$: chrome.breadcrumbs.get$(), navLinks, navControls, homeHref diff --git a/src/ui/public/chrome/directives/header_global_nav/index.ts b/src/ui/public/chrome/directives/header_global_nav/index.ts index 9b6b7521c2c4..43e0d04916b7 100644 --- a/src/ui/public/chrome/directives/header_global_nav/index.ts +++ b/src/ui/public/chrome/directives/header_global_nav/index.ts @@ -38,8 +38,3 @@ export interface NavLink { id: string; euiIconType: IconType; } - -export interface Breadcrumb { - text: string; - href?: string; -} diff --git a/src/ui/public/chrome/services/breadcrumb_state.ts b/src/ui/public/chrome/services/breadcrumb_state.ts deleted file mode 100644 index 020c9f133396..000000000000 --- a/src/ui/public/chrome/services/breadcrumb_state.ts +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { Subject, Subscribable } from 'rxjs'; -// @ts-ignore -import { uiModules } from '../../modules'; -import { Breadcrumb } from '../directives/header_global_nav'; - -// A flag used to keep track of clearing between route changes. -let shouldClear = false; - -// Subject used by Header component to subscribe to breadcrumbs changes. -// This is not exposed publicly. -const breadcrumbsSubject = new Subject(); - -/** - * A rxjs subscribable that can be used to subscribe to breadcrumb updates. - */ -export const breadcrumbs: Subscribable = breadcrumbsSubject; - -/** - * Should be called by plugins to set breadcrumbs in the header navigation. - * - * @param breadcrumbs: Array where Breadcrumb has shape - * { text: '', href?: '' } - */ -export const set = (newBreadcrumbs: Breadcrumb[]) => { - breadcrumbsSubject.next(newBreadcrumbs); - - // If a plugin called set, don't clear on route change. - shouldClear = false; -}; - -uiModules.get('kibana').service('breadcrumbState', ($rootScope: any) => { - // When a route change happens we want to clear the breadcrumbs ONLY if - // the new route does not set any breadcrumbs. Deferring the clearing until - // the route finishes changing helps avoiding the breadcrumbs from 'flickering'. - $rootScope.$on('$routeChangeStart', () => (shouldClear = true)); - $rootScope.$on('$routeChangeSuccess', () => { - if (shouldClear) { - set([]); - } - }); - - return { set }; -}); diff --git a/src/ui/public/chrome/services/index.js b/src/ui/public/chrome/services/index.js index 3c2f2fb71d20..3b3967f51b2f 100644 --- a/src/ui/public/chrome/services/index.js +++ b/src/ui/public/chrome/services/index.js @@ -18,4 +18,3 @@ */ import './global_nav_state'; -import './breadcrumb_state'; diff --git a/src/ui/public/kbn_top_nav/bread_crumbs/bread_crumbs.js b/src/ui/public/kbn_top_nav/bread_crumbs/bread_crumbs.js index d0674cc68d2d..0c6999388749 100644 --- a/src/ui/public/kbn_top_nav/bread_crumbs/bread_crumbs.js +++ b/src/ui/public/kbn_top_nav/bread_crumbs/bread_crumbs.js @@ -20,6 +20,7 @@ import breadCrumbsTemplate from './bread_crumbs.html'; import { uiModules } from '../../modules'; import uiRouter from '../../routes'; +import chrome from '../../chrome'; const module = uiModules.get('kibana'); @@ -46,7 +47,7 @@ module.directive('breadCrumbs', function () { useLinks: '=' }, template: breadCrumbsTemplate, - controller: function ($scope, config, breadcrumbState) { + controller: function ($scope, config) { config.watch('k7design', (val) => $scope.showPluginBreadcrumbs = !val); function omitPagesFilter(crumb) { @@ -78,7 +79,7 @@ module.directive('breadCrumbs', function () { newBreadcrumbs.push({ text: $scope.pageTitle }); } - breadcrumbState.set(newBreadcrumbs); + chrome.breadcrumbs.set(newBreadcrumbs); }); } }; diff --git a/x-pack/plugins/apm/public/components/app/Main/Breadcrumbs.js b/x-pack/plugins/apm/public/components/app/Main/Breadcrumbs.js index ba4fd5ed004a..3c3a9568e5d4 100644 --- a/x-pack/plugins/apm/public/components/app/Main/Breadcrumbs.js +++ b/x-pack/plugins/apm/public/components/app/Main/Breadcrumbs.js @@ -6,10 +6,12 @@ import React from 'react'; import { withBreadcrumbs } from 'react-router-breadcrumbs-hoc'; +import { flatten, capitalize } from 'lodash'; + +import chrome from 'ui/chrome'; + import { toQuery } from '../../../utils/url'; import { routes } from './routeConfig'; -import { flatten, capitalize } from 'lodash'; -import { set } from 'ui/chrome/services/breadcrumb_state'; class Breadcrumbs extends React.Component { updateHeaderBreadcrumbs() { @@ -19,7 +21,7 @@ class Breadcrumbs extends React.Component { href: `#${match.url}?_g=${_g}&kuery=${kuery}` })); - set(breadcrumbs); + chrome.breadcrumbs.set(breadcrumbs); } componentDidMount() { 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__/Breadcrumbs.test.js index ac2a45350997..be57500928f4 100644 --- a/x-pack/plugins/apm/public/components/app/Main/__test__/Breadcrumbs.test.js +++ b/x-pack/plugins/apm/public/components/app/Main/__test__/Breadcrumbs.test.js @@ -14,6 +14,9 @@ import { toJson } from '../../../../utils/testHelpers'; jest.mock( 'ui/chrome', () => ({ + breadcrumbs: { + set: () => {} + }, getBasePath: () => `/some/base/path`, getUiSettingsClient: () => { return { diff --git a/x-pack/plugins/ml/public/components/nav_menu/nav_menu.js b/x-pack/plugins/ml/public/components/nav_menu/nav_menu.js index 2ead5f96dd40..12425b7fa6c5 100644 --- a/x-pack/plugins/ml/public/components/nav_menu/nav_menu.js +++ b/x-pack/plugins/ml/public/components/nav_menu/nav_menu.js @@ -10,12 +10,13 @@ import _ from 'lodash'; import $ from 'jquery'; import template from './nav_menu.html'; import uiRouter from 'ui/routes'; +import chrome from 'ui/chrome'; import { isFullLicense } from '../../license/check_license'; import { uiModules } from 'ui/modules'; const module = uiModules.get('apps/ml'); -module.directive('mlNavMenu', function (breadcrumbState, config) { +module.directive('mlNavMenu', function (config) { return { restrict: 'E', transclude: true, @@ -73,7 +74,7 @@ module.directive('mlNavMenu', function (breadcrumbState, config) { scope.breadcrumbs = breadcrumbs.filter(Boolean); config.watch('k7design', (val) => scope.showPluginBreadcrumbs = !val); - breadcrumbState.set(scope.breadcrumbs.map(b => ({ text: b.label, href: b.url }))); + chrome.breadcrumbs.set(scope.breadcrumbs.map(b => ({ text: b.label, href: b.url }))); // when the page loads, focus on the first breadcrumb el.ready(() => { diff --git a/x-pack/plugins/monitoring/public/services/breadcrumbs_provider.js b/x-pack/plugins/monitoring/public/services/breadcrumbs_provider.js index 1b1cff5652af..8d3adcb541e9 100644 --- a/x-pack/plugins/monitoring/public/services/breadcrumbs_provider.js +++ b/x-pack/plugins/monitoring/public/services/breadcrumbs_provider.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { set as setBreadcrumbs } from 'ui/chrome/services/breadcrumb_state'; +import chrome from 'ui/chrome'; import { i18n } from '@kbn/i18n'; // Helper for making objects to use in a link element @@ -147,7 +147,7 @@ export function breadcrumbsProvider() { breadcrumbs = breadcrumbs.concat(getApmBreadcrumbs(mainInstance)); } - setBreadcrumbs(breadcrumbs.map(b => ({ text: b.label, href: b.url }))); + chrome.breadcrumbs.set(breadcrumbs.map(b => ({ text: b.label, href: b.url }))); return breadcrumbs; };