diff --git a/src/ui/public/chrome/directives/__tests__/sub_url_route_filter.js b/src/ui/public/chrome/directives/__tests__/sub_url_route_filter.js
new file mode 100644
index 00000000000000..e4b27b5059950c
--- /dev/null
+++ b/src/ui/public/chrome/directives/__tests__/sub_url_route_filter.js
@@ -0,0 +1,105 @@
+import ngMock from 'ng_mock';
+import expect from 'expect.js';
+
+import { SubUrlRouteFilterProvider } from '../sub_url_route_filter';
+
+describe('kbn-chrome subUrlRouteFilter()', () => {
+ describe('no ngRoute', () => {
+ beforeEach(ngMock.module('kibana/private'));
+ beforeEach(ngMock.inject(($injector) => {
+ expect($injector.has('$route')).to.be(false);
+ }));
+
+ it('always returns true when there is no $route service', ngMock.inject((Private) => {
+ const subUrlRouteFilter = Private(SubUrlRouteFilterProvider);
+ expect(subUrlRouteFilter()).to.be(true);
+ }));
+ });
+
+ describe('with ngRoute', () => {
+
+ beforeEach(ngMock.module('kibana/private', 'ngRoute', $routeProvider => {
+ $routeProvider.when('/foo', {
+ redirectTo: '/bar'
+ });
+
+ $routeProvider.when('/bar', {
+ template: '
foo => bar
'
+ });
+ }));
+
+ let test;
+ beforeEach(ngMock.inject((Private, $location, $rootScope, $route) => {
+ test = ({ path, assert }) => {
+ const subUrlRouteFilter = Private(SubUrlRouteFilterProvider);
+ $location.path(path);
+
+ let result;
+ function runAssert() {
+ if (result) {
+ // only run once
+ return;
+ }
+
+ try {
+ assert($route, subUrlRouteFilter);
+ result = {};
+ } catch (error) {
+ result = { error };
+ }
+ }
+
+ $rootScope.$on('$routeUpdate', runAssert);
+ $rootScope.$on('$routeChangeSuccess', runAssert);
+ $rootScope.$apply();
+
+ // when no route matches there is no event so we run manually
+ if (!result) {
+ runAssert();
+ }
+
+ if (result.error) {
+ throw result.error;
+ }
+ };
+ }));
+
+ describe('no current route', () => {
+ it('returns false', () => {
+ test({
+ path: '/baz',
+ assert($route, subUrlRouteFilter) {
+ expect($route.current).to.not.be.ok();
+ expect(subUrlRouteFilter()).to.eql(false);
+ }
+ });
+ });
+ });
+
+ describe('redirectTo route', () => {
+ it('returns false', () => {
+ test({
+ path: '/foo',
+ assert($route, subUrlRouteFilter) {
+ expect($route.current).to.be.ok();
+ expect($route.current.redirectTo).to.be.ok();
+ expect(subUrlRouteFilter()).to.eql(false);
+ }
+ });
+ });
+ });
+
+ describe('standard route', () => {
+ it('returns true', () => {
+ test({
+ path: '/bar',
+ assert($route, subUrlRouteFilter) {
+ expect($route.current).to.be.ok();
+ expect($route.current.template).to.be.ok();
+ expect(subUrlRouteFilter()).to.eql(true);
+ }
+ });
+ });
+ });
+ });
+});
diff --git a/src/ui/public/chrome/directives/kbn_chrome.js b/src/ui/public/chrome/directives/kbn_chrome.js
index 0c5cbe2e9503ff..c3fbc86d1d0276 100644
--- a/src/ui/public/chrome/directives/kbn_chrome.js
+++ b/src/ui/public/chrome/directives/kbn_chrome.js
@@ -9,6 +9,7 @@ import {
unhashUrl,
} from 'ui/state_management/state_hashing';
import { notify } from 'ui/notify';
+import { SubUrlRouteFilterProvider } from './sub_url_route_filter';
export function kbnChromeProvider(chrome, internals) {
@@ -39,16 +40,23 @@ export function kbnChromeProvider(chrome, internals) {
// are we showing the embedded version of the chrome?
internals.setVisibleDefault(!$location.search().embed);
- // listen for route changes, propogate to tabs
- const onRouteChange = function () {
+ const subUrlRouteFilter = Private(SubUrlRouteFilterProvider);
+
+ function updateSubUrls() {
const urlWithHashes = window.location.href;
const urlWithStates = unhashUrl(urlWithHashes, getUnhashableStates());
internals.trackPossibleSubUrl(urlWithStates);
- };
+ }
+
+ function onRouteChange($event) {
+ if (subUrlRouteFilter($event)) {
+ updateSubUrls();
+ }
+ }
$rootScope.$on('$routeChangeSuccess', onRouteChange);
$rootScope.$on('$routeUpdate', onRouteChange);
- onRouteChange();
+ updateSubUrls(); // initialize sub urls
const allPendingHttpRequests = () => $http.pendingRequests;
const removeSystemApiRequests = (pendingHttpRequests = []) => remove(pendingHttpRequests, isSystemApiRequest);
diff --git a/src/ui/public/chrome/directives/sub_url_route_filter.js b/src/ui/public/chrome/directives/sub_url_route_filter.js
new file mode 100644
index 00000000000000..ccfc68c834f670
--- /dev/null
+++ b/src/ui/public/chrome/directives/sub_url_route_filter.js
@@ -0,0 +1,18 @@
+/**
+ * Creates a function that will be called on each route change
+ * to determine if the event should be used to update the last
+ * subUrl of chrome links/tabs
+ * @injected
+ */
+export function SubUrlRouteFilterProvider($injector) {
+ if (!$injector.has('$route')) {
+ return function alwaysUpdate() {
+ return true;
+ };
+ }
+
+ const $route = $injector.get('$route');
+ return function ignoreRedirectToRoutes() {
+ return Boolean($route.current && !$route.current.redirectTo);
+ };
+}
diff --git a/src/ui/public/private/private.js b/src/ui/public/private/private.js
index e4f2660866c7da..fca8a12bff4a04 100644
--- a/src/ui/public/private/private.js
+++ b/src/ui/public/private/private.js
@@ -93,7 +93,7 @@ function name(fn) {
return fn.name || fn.toString().split('\n').shift();
}
-uiModules.get('kibana')
+uiModules.get('kibana/private')
.provider('Private', function () {
const provider = this;