Merge pull request #4755 from spalger/implement/exportLinks

Properly merge global state into links
This commit is contained in:
Spencer 2016-03-30 09:43:16 -07:00
commit b475997dab
7 changed files with 101 additions and 26 deletions

View file

@ -1,9 +1,9 @@
const store = Symbol('store');
export default class TabFakeStore {
export default class StubBrowserStorage {
constructor() { this[store] = new Map(); }
getItem(k) { return this[store].get(k); }
setItem(k, v) { return this[store].set(k, v); }
setItem(k, v) { return this[store].set(k, String(v)); }
removeItem(k) { return this[store].delete(k); }
getKeys() { return [ ...this[store].keys() ]; }
getValues() { return [ ...this[store].values() ]; }

View file

@ -1,7 +1,8 @@
import sinon from 'auto-release-sinon';
import Tab from '../tab';
import expect from 'expect.js';
import TabFakeStore from './_tab_fake_store';
import StubBrowserStorage from './fixtures/stub_browser_storage';
describe('Chrome Tab', function () {
describe('construction', function () {
@ -88,7 +89,7 @@ describe('Chrome Tab', function () {
});
it('discovers the lastUrl', function () {
const lastUrlStore = new TabFakeStore();
const lastUrlStore = new StubBrowserStorage();
const tab = new Tab({ id: 'foo', lastUrlStore });
expect(tab.lastUrl).to.not.equal('/foo/bar');
@ -100,7 +101,7 @@ describe('Chrome Tab', function () {
});
it('logs a warning about last urls that do not match the rootUrl', function () {
const lastUrlStore = new TabFakeStore();
const lastUrlStore = new StubBrowserStorage();
const tab = new Tab({ id: 'foo', baseUrl: '/bar', lastUrlStore });
tab.setLastUrl('/bar/foo/1');
@ -114,7 +115,7 @@ describe('Chrome Tab', function () {
describe('#setLastUrl()', function () {
it('updates the lastUrl and storage value if passed a lastUrlStore', function () {
const lastUrlStore = new TabFakeStore();
const lastUrlStore = new StubBrowserStorage();
const tab = new Tab({ id: 'foo', lastUrlStore });
expect(tab.lastUrl).to.not.equal('foo');

View file

@ -1,6 +1,6 @@
import expect from 'expect.js';
import TabFakeStore from './_tab_fake_store';
import StubBrowserStorage from './fixtures/stub_browser_storage';
import TabCollection from '../tab_collection';
import Tab from '../tab';
import { indexBy, random } from 'lodash';
@ -54,7 +54,7 @@ describe('Chrome TabCollection', function () {
describe('#consumeRouteUpdate()', function () {
it('updates the active tab', function () {
const store = new TabFakeStore();
const store = new StubBrowserStorage();
const baseUrl = `http://localhost:${random(1000, 9999)}`;
const tabs = new TabCollection({ store, defaults: { baseUrl } });
tabs.set([

View file

@ -1,7 +1,6 @@
import expect from 'expect.js';
import kbnAngular from '../angular';
import TabFakeStore from '../../__tests__/_tab_fake_store';
import { noop } from 'lodash';
describe('Chrome API :: Angular', () => {

View file

@ -1,7 +1,7 @@
import expect from 'expect.js';
import setup from '../apps';
import TabFakeStore from '../../__tests__/_tab_fake_store';
import StubBrowserStorage from '../../__tests__/fixtures/stub_browser_storage';
describe('Chrome API :: apps', function () {
describe('#get/setShowAppsLink()', function () {
@ -147,7 +147,7 @@ describe('Chrome API :: apps', function () {
describe('#get/setLastUrlFor()', function () {
it('reads/writes last url from storage', function () {
const chrome = {};
const store = new TabFakeStore();
const store = new StubBrowserStorage();
setup(chrome, { appUrlStore: store });
expect(chrome.getLastUrlFor('app')).to.equal(undefined);
chrome.setLastUrlFor('app', 'url');

View file

@ -1,45 +1,73 @@
import expect from 'expect.js';
import initChromeNavApi from 'ui/chrome/api/nav';
import StubBrowserStorage from '../../__tests__/fixtures/stub_browser_storage';
const basePath = '/someBasePath';
function getChrome(customInternals = { basePath }) {
function init(customInternals = { basePath }) {
const chrome = {};
initChromeNavApi(chrome, {
const internals = {
nav: [],
...customInternals,
});
return chrome;
};
initChromeNavApi(chrome, internals);
return { chrome, internals };
}
describe('chrome nav apis', function () {
describe('#getBasePath()', function () {
it('returns the basePath', function () {
const chrome = getChrome();
const { chrome } = init();
expect(chrome.getBasePath()).to.be(basePath);
});
});
describe('#addBasePath()', function () {
it('returns undefined when nothing is passed', function () {
const chrome = getChrome();
const { chrome } = init();
expect(chrome.addBasePath()).to.be(undefined);
});
it('prepends the base path when the input is a path', function () {
const chrome = getChrome();
const { chrome } = init();
expect(chrome.addBasePath('/other/path')).to.be(`${basePath}/other/path`);
});
it('ignores non-path urls', function () {
const chrome = getChrome();
const { chrome } = init();
expect(chrome.addBasePath('http://github.com/elastic/kibana')).to.be('http://github.com/elastic/kibana');
});
it('includes the query string', function () {
const chrome = getChrome();
const { chrome } = init();
expect(chrome.addBasePath('/app/kibana?a=b')).to.be(`${basePath}/app/kibana?a=b`);
});
});
describe('internals.trackPossibleSubUrl()', function () {
it('injects the globalState of the current url to all links for the same app', function () {
const appUrlStore = new StubBrowserStorage();
const nav = [
{ url: 'https://localhost:9200/app/kibana#discover' },
{ url: 'https://localhost:9200/app/kibana#visualize' },
{ url: 'https://localhost:9200/app/kibana#dashboard' },
].map(l => {
l.lastSubUrl = l.url;
return l;
});
const { chrome, internals } = init({ appUrlStore, nav });
internals.trackPossibleSubUrl('https://localhost:9200/app/kibana#dashboard?_g=globalstate');
expect(internals.nav[0].lastSubUrl).to.be('https://localhost:9200/app/kibana#discover?_g=globalstate');
expect(internals.nav[0].active).to.be(false);
expect(internals.nav[1].lastSubUrl).to.be('https://localhost:9200/app/kibana#visualize?_g=globalstate');
expect(internals.nav[1].active).to.be(false);
expect(internals.nav[2].lastSubUrl).to.be('https://localhost:9200/app/kibana#dashboard?_g=globalstate');
expect(internals.nav[2].active).to.be(true);
});
});
});

View file

@ -40,25 +40,72 @@ export default function (chrome, internals) {
}
function refreshLastUrl(link) {
link.lastSubUrl = internals.appUrlStore.getItem(lastSubUrlKey(link));
link.lastSubUrl = internals.appUrlStore.getItem(lastSubUrlKey(link)) || link.lastSubUrl || link.url;
}
function getAppId(url) {
const pathname = parse(url).pathname;
const pathnameWithoutBasepath = pathname.slice(chrome.getBasePath().length);
const match = pathnameWithoutBasepath.match(/^\/app\/([^\/]+)(?:\/|\?|#|$)/);
if (match) return match[1];
}
function decodeKibanaUrl(url) {
const parsedUrl = parse(url, true);
const appId = getAppId(parsedUrl);
const hash = parsedUrl.hash || '';
const parsedHash = parse(hash.slice(1), true);
const globalState = parsedHash.query && parsedHash.query._g;
return { appId, globalState, parsedUrl, parsedHash };
}
function injectNewGlobalState(link, fromAppId, newGlobalState) {
// parse the lastSubUrl of this link so we can manipulate its parts
const { appId: toAppId, parsedHash: toHash, parsedUrl: toParsed } = decodeKibanaUrl(link.lastSubUrl);
// don't copy global state if links are for different apps
if (fromAppId !== toAppId) return;
// add the new globalState to the hashUrl in the linkurl
const toHashQuery = toHash.query || {};
toHashQuery._g = newGlobalState;
// format the new subUrl and include the newHash
link.lastSubUrl = format({
protocol: toParsed.protocol,
port: toParsed.port,
hostname: toParsed.hostname,
pathname: toParsed.pathname,
query: toParsed.query,
hash: format({
pathname: toHash.pathname,
query: toHashQuery,
hash: toHash.hash,
}),
});
}
internals.trackPossibleSubUrl = function (url) {
for (const link of internals.nav) {
link.active = startsWith(url, link.url);
const { appId, globalState: newGlobalState } = decodeKibanaUrl(url);
for (const link of internals.nav) {
const matchingTab = find(internals.tabs, { rootUrl: link.url });
link.active = startsWith(url, link.url);
if (link.active) {
setLastUrl(link, url);
continue;
}
const matchingTab = find(internals.tabs, { rootUrl: link.url });
if (matchingTab) {
setLastUrl(link, matchingTab.getLastUrl());
continue;
} else {
refreshLastUrl(link);
}
refreshLastUrl(link);
if (newGlobalState) {
injectNewGlobalState(link, appId, newGlobalState);
}
}
};