Merge pull request #6170 from panda01/fix/nav-details

Fix/nav details
This commit is contained in:
Khalah Jones Golden 2016-02-10 19:47:24 -04:00
commit af5f0b6a2c
29 changed files with 462 additions and 133 deletions

View file

@ -2,7 +2,7 @@ import ingest from './server/routes/api/ingest';
module.exports = function (kibana) {
return new kibana.Plugin({
id: 'kibana',
config: function (Joi) {
return Joi.object({
enabled: Joi.boolean().default(true),
@ -13,7 +13,9 @@ module.exports = function (kibana) {
uiExports: {
app: {
id: 'kibana',
title: 'Kibana',
listed: false,
description: 'the kibana you know and love',
//icon: 'plugins/kibana/settings/sections/about/barcode.svg',
main: 'plugins/kibana/kibana',
@ -32,8 +34,39 @@ module.exports = function (kibana) {
return {
kbnDefaultAppId: config.get('kibana.defaultAppId')
};
},
},
links: [
{
title: 'Discover',
order: -1003,
url: '/app/kibana#/discover',
description: 'interactively explore your data',
icon: 'plugins/kibana/assets/discover.svg',
},
{
title: 'Visualize',
order: -1002,
url: '/app/kibana#/visualize',
description: 'design data visualizations',
icon: 'plugins/kibana/assets/visualize.svg',
},
{
title: 'Dashboard',
order: -1001,
url: '/app/kibana#/dashboard',
description: 'compose visualizations for much win',
icon: 'plugins/kibana/assets/dashboard.svg',
},
{
title: 'Settings',
order: 1000,
url: '/app/kibana#/settings',
description: 'define index patterns, change config, and more',
icon: 'plugins/kibana/assets/settings.svg',
}
}
]
},
init: function (server, options) {

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M4 0c-2.2 0-4 1.8-4 4s1.8 4 4 4 4-1.8 4-4-1.8-4-4-4zm0 1c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 1c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm-1.66 1a.5.5 0 0 0-.19.84l.91.91c-.02.08-.06.16-.06.25 0 .55.45 1 1 1s1-.45 1-1-.45-1-1-1c-.09 0-.17.04-.25.06l-.91-.91a.5.5 0 0 0-.44-.16.5.5 0 0 0-.06 0zm3.16 0c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5z"
/>
</svg>

After

Width:  |  Height:  |  Size: 486 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M4 0c-2.2 0-4 1.8-4 4s1.8 4 4 4 4-1.8 4-4-1.8-4-4-4zm0 1c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm2 1l-3 1-1 3 3-1 1-3zm-2 1.5c.28 0 .5.22.5.5s-.22.5-.5.5-.5-.22-.5-.5.22-.5.5-.5z" />
</svg>

After

Width:  |  Height:  |  Size: 293 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M3 0v1h4v5h-4v1h5v-7h-5zm-1 2l-2 1.5 2 1.5v-1h4v-1h-4v-1z" />
</svg>

After

Width:  |  Height:  |  Size: 159 B

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M3.5 0c-1.93 0-3.5 1.57-3.5 3.5s1.57 3.5 3.5 3.5c.59 0 1.17-.14 1.66-.41a1 1 0 0 0 .13.13l1 1a1.02 1.02 0 1 0 1.44-1.44l-1-1a1 1 0 0 0-.16-.13c.27-.49.44-1.06.44-1.66 0-1.93-1.57-3.5-3.5-3.5zm0 1c1.39 0 2.5 1.11 2.5 2.5 0 .66-.24 1.27-.66 1.72-.01.01-.02.02-.03.03a1 1 0 0 0-.13.13c-.44.4-1.04.63-1.69.63-1.39 0-2.5-1.11-2.5-2.5s1.11-2.5 2.5-2.5z"
/>
</svg>

After

Width:  |  Height:  |  Size: 450 B

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M3.5 0l-.5 1.19c-.1.03-.19.08-.28.13l-1.19-.5-.72.72.5 1.19c-.05.1-.09.18-.13.28l-1.19.5v1l1.19.5c.04.1.08.18.13.28l-.5 1.19.72.72 1.19-.5c.09.04.18.09.28.13l.5 1.19h1l.5-1.19c.09-.04.19-.08.28-.13l1.19.5.72-.72-.5-1.19c.04-.09.09-.19.13-.28l1.19-.5v-1l-1.19-.5c-.03-.09-.08-.19-.13-.28l.5-1.19-.72-.72-1.19.5c-.09-.04-.19-.09-.28-.13l-.5-1.19h-1zm.5 2.5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5-1.5-.67-1.5-1.5.67-1.5 1.5-1.5z"
/>
</svg>

After

Width:  |  Height:  |  Size: 528 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M0 0v7h8v-1h-7v-6h-1zm5 0v5h2v-5h-2zm-3 2v3h2v-3h-2z" />
</svg>

After

Width:  |  Height:  |  Size: 154 B

View file

@ -28,13 +28,13 @@ routes
chrome
.setBrand({
'logo': 'url(' + kibanaLogoUrl + ') center / 160px 70px no-repeat #e8488b',
'smallLogo': 'url(' + kibanaLogoUrl + ') center / 160px 70px no-repeat #e8488b'
'logo': 'url(' + kibanaLogoUrl + ') 6px 10px / 140px 50px no-repeat #e8488b',
'smallLogo': 'url(' + kibanaLogoUrl + ') 6px 10px / 140px 50px no-repeat #e8488b'
})
.setNavBackground('#222222')
.setTabDefaults({
resetWhenActive: true,
lastUrlStore: window.sessionStore,
lastUrlStore: window.sessionStorage,
activeIndicatorColor: '#656a76'
})
.setTabs([

View file

@ -54,3 +54,8 @@ const chrome = require('ui/chrome')
ui.refresh();
});
require('ui/modules').get('kibana')
.config(function (appSwitcherEnsureNavigationProvider) {
appSwitcherEnsureNavigationProvider.forceNavigation(true);
});

View file

@ -71,7 +71,7 @@ module.exports = async (kbnServer, server, config) => {
server.decorate('reply', 'renderApp', function (app) {
const payload = {
app: app,
nav: uiExports.apps,
nav: uiExports.navLinks.inOrder,
version: kbnServer.version,
buildNum: config.get('pkg.buildNum'),
buildSha: config.get('pkg.buildSha'),

View file

@ -1,3 +1,4 @@
import notify from 'ui/notify';
import _ from 'lodash';
import { escapeRegExp as reEsc } from 'lodash';
import { parse, format } from 'url';
@ -28,7 +29,16 @@ export default class Tab {
this.lastUrlStoreKey = `lastUrl:${this.id}`;
this.lastUrlStore = spec.lastUrlStore;
this.lastUrl = this.lastUrlStore ? this.lastUrlStore.getItem(this.lastUrlStoreKey) : null;
this.lastUrl = null;
if (this.lastUrlStore) {
this.lastUrl = this.lastUrlStore.getItem(this.lastUrlStoreKey);
if (this.lastUrl && !this.lastUrl.startsWith(this.rootUrl)) {
notify.log(`Found invalid lastUrl for tab with root url ${this.rootUrl}: "${this.lastUrl}"`);
this.lastUrl = null;
this.lastUrlStore.removeItem(this.lastUrlStoreKey);
}
}
}
href() {
@ -54,7 +64,8 @@ export default class Tab {
let lastUrl = this.getLastUrl();
if (!lastUrl.startsWith(rootUrl)) {
throw new Error(`Tab "${id}" has invalid root "${rootUrl}" for last url "${lastUrl}"`);
notify.log(`Tab "${id}" has invalid root "${rootUrl}" for last url "${lastUrl}"`);
lastUrl = rootUrl;
}
return lastUrl.slice(rootUrl.length);

View file

@ -1,3 +1,4 @@
import sinon from 'auto-release-sinon';
import Tab from '../Tab';
import expect from 'expect.js';
import TabFakeStore from './_TabFakeStore';
@ -89,13 +90,24 @@ describe('Chrome Tab', function () {
it('discovers the lastUrl', function () {
const lastUrlStore = new TabFakeStore();
const tab = new Tab({ id: 'foo', lastUrlStore });
expect(tab.lastUrl).to.not.equal('bar');
expect(tab.lastUrl).to.not.equal('/foo/bar');
tab.setLastUrl('bar');
expect(tab.lastUrl).to.equal('bar');
tab.setLastUrl('/foo/bar');
expect(tab.lastUrl).to.equal('/foo/bar');
const tab2 = new Tab({ id: 'foo', lastUrlStore });
expect(tab2.lastUrl).to.equal('bar');
expect(tab2.lastUrl).to.equal('/foo/bar');
});
it('logs a warning about last urls that do not match the rootUrl', function () {
const lastUrlStore = new TabFakeStore();
const tab = new Tab({ id: 'foo', baseUrl: '/bar', lastUrlStore });
tab.setLastUrl('/bar/foo/1');
const stub = sinon.stub(console, 'log');
const tab2 = new Tab({ id: 'foo', baseUrl: '/baz', lastUrlStore });
sinon.assert.calledOnce(stub);
expect(tab2.lastUrl).to.equal(null);
});
});
@ -167,14 +179,14 @@ describe('Chrome Tab', function () {
expect(tab.getLastPath()).to.equal('/index');
});
it('throws an error if the lastUrl does not extend the root url', function () {
expect(function () {
const baseUrl = 'http://local:5601/app/visualize#';
const tab = new Tab({ baseUrl });
it('logs a warning if the lastUrl does not extend the root url', function () {
const baseUrl = 'http://local:5601/app/visualize#';
const tab = new Tab({ baseUrl });
sinon.stub(console, 'log');
tab.setLastUrl('http://local:5601/');
tab.getLastPath();
}).to.throwError(/invalid.*root/);
tab.setLastUrl('http://local:5601/');
tab.getLastPath();
sinon.assert.calledOnce(console.log);// eslint-disable-line no-console
});
});

View file

@ -4,6 +4,7 @@ export default class TabFakeStore {
constructor() { this[store] = new Map(); }
getItem(k) { return this[store].get(k); }
setItem(k, v) { return this[store].set(k, v); }
removeItem(k) { return this[store].delete(k); }
getKeys() { return [ ...this[store].keys() ]; }
getValues() { return [ ...this[store].values() ]; }
}

View file

@ -86,11 +86,11 @@ describe('Chrome API :: apps', function () {
describe('#getAppUrl()', function () {
it('returns the resolved url of the current app', function () {
const chrome = {};
const app = { url: '/foo' };
const app = { navLink: { url: '/foo' } };
setup(chrome, { app });
const a = document.createElement('a');
a.setAttribute('href', app.url);
a.setAttribute('href', app.navLink.url);
expect(chrome.getAppUrl()).to.equal(a.href);
});

View file

@ -3,8 +3,8 @@ import { resolve } from 'url';
module.exports = function (chrome, internals) {
if (internals.app) {
internals.app.url = resolve(window.location.href, internals.app.url);
if (get(internals, 'app.navLink.url')) {
internals.app.navLink.url = resolve(window.location.href, internals.app.navLink.url);
}
internals.appUrlStore = internals.appUrlStore || window.sessionStorage;
@ -35,7 +35,7 @@ module.exports = function (chrome, internals) {
};
chrome.getAppUrl = function () {
return get(internals, ['app', 'url']);
return get(internals, ['app', 'navLink', 'url']);
};
chrome.getInjected = function (name, def) {

View file

@ -1,15 +1,11 @@
import { parse, format } from 'url';
import { startsWith, isString } from 'lodash';
import { startsWith, isString, find } from 'lodash';
export default function (chrome, internals) {
chrome.getNavLinks = function () {
return internals.nav;
};
chrome.getLastSubUrlFor = function (url) {
return internals.appUrlStore.getItem(`lastSubUrl:${url}`);
};
chrome.getBasePath = function () {
return internals.basePath || '';
};
@ -34,26 +30,46 @@ export default function (chrome, internals) {
});
};
function lastSubUrlKey(link) {
return `lastSubUrl:${link.url}`;
}
function setLastUrl(link, url) {
link.lastSubUrl = url;
internals.appUrlStore.setItem(lastSubUrlKey(link), url);
}
function refreshLastUrl(link) {
link.lastSubUrl = internals.appUrlStore.getItem(lastSubUrlKey(link));
}
internals.trackPossibleSubUrl = function (url) {
for (const link of internals.nav) {
if (startsWith(url, link.url)) {
link.lastSubUrl = url;
internals.appUrlStore.setItem(`lastSubUrl:${link.url}`, 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;
}
refreshLastUrl(link);
}
};
internals.nav.forEach(link => {
// convert all link urls to absolute urls
var a = document.createElement('a');
a.setAttribute('href', link.url);
link.url = a.href;
link.lastSubUrl = chrome.getLastSubUrlFor(link.url);
if (link.url === chrome.getAppUrl()) {
link.active = true;
}
});
// simulate a possible change in url to initialize the
// link.active and link.lastUrl properties
internals.trackPossibleSubUrl(document.location.href);
};

View file

@ -43,51 +43,53 @@
</nav>
<div class="app-wrapper">
<kbn-notifications list="notifList"></kbn-notifications>
<nav
ng-style="::{ background: chrome.getNavBackground() }"
ng-class="{ show: chrome.getVisible() }"
class="hide navbar navbar-inverse navbar-static-top">
<div class="app-wrapper-panel">
<kbn-notifications list="notifList"></kbn-notifications>
<nav
ng-style="::{ background: chrome.getNavBackground() }"
ng-class="{ show: chrome.getVisible() }"
class="hide navbar navbar-inverse navbar-static-top">
<!-- Mobile navbar -->
<div class="navbar-header">
<button ng-click="showCollapsed = !showCollapsed" type="button" class="navbar-toggle">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<span class="visible-xs">
<span ng-if="chrome.getBrand('title')" class="navbar-brand">{{ chrome.getBrand('title') }}</span>
<span ng-if="chrome.getActiveTabTitle()" class="navbar-brand">{{ chrome.getActiveTabTitle() }}</span>
<span ng-show="chrome.httpActive.length" class="spinner"></span>
</span>
</div>
<!-- /Mobile navbar -->
<!-- Mobile navbar -->
<div class="navbar-header">
<button ng-click="showCollapsed = !showCollapsed" type="button" class="navbar-toggle">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<span class="visible-xs">
<span ng-if="chrome.getBrand('title')" class="navbar-brand">{{ chrome.getBrand('title') }}</span>
<span ng-if="chrome.getActiveTabTitle()" class="navbar-brand">{{ chrome.getActiveTabTitle() }}</span>
<span ng-show="chrome.httpActive.length" class="spinner"></span>
</span>
</div>
<!-- /Mobile navbar -->
<!-- Full navbar -->
<div collapse="!showCollapsed" class="navbar-collapse" kbn-chrome-append-nav-controls>
<ul class="nav navbar-nav" role="navigation">
<!-- Full navbar -->
<div collapse="!showCollapsed" class="navbar-collapse" kbn-chrome-append-nav-controls>
<ul class="nav navbar-nav" role="navigation">
<li ng-if="chrome.getBrand('title')" class="navbar-brand">{{ chrome.getBrand('title') }}</li>
<li ng-if="chrome.getBrand('title')" class="navbar-brand">{{ chrome.getBrand('title') }}</li>
<li ng-repeat="tab in chrome.getTabs()" ng-class="{ active: tab.active }">
<a ng-href="{{ tab.href() }}" ng-style="{ 'border-bottom-color': tab.activeIndicatorColor }">
{{ tab.title }}
</a>
</li>
</ul>
</div>
<!-- /Full navbar -->
</nav>
<li ng-repeat="tab in chrome.getTabs()" ng-class="{ active: tab.active }">
<a ng-href="{{ tab.href() }}" ng-style="{ 'border-bottom-color': tab.activeIndicatorColor }">
{{ tab.title }}
</a>
</li>
</ul>
</div>
<!-- /Full navbar -->
</nav>
<config
ng-show="timefilter.enabled"
config-template="pickerTemplate"
config-object="timefilter"
config-close="pickerTemplate.close">
</config>
<config
ng-show="timefilter.enabled"
config-template="pickerTemplate"
config-object="timefilter"
config-close="pickerTemplate.close">
</config>
<div class="application" ng-class="'tab-' + chrome.getActiveTabId('-none-') + ' ' + chrome.getApplicationClasses()" ng-view></div>
<div class="application" ng-class="'tab-' + chrome.getActiveTabId('-none-') + ' ' + chrome.getApplicationClasses()" ng-view></div>
</div>
</div>
</div>

View file

@ -1,7 +1,7 @@
<div class="app-links">
<div
class="app-link"
ng-repeat="link in switcher.getNavLinks() | orderBy:'title'"
ng-repeat="link in switcher.getNavLinks()"
ng-class="{ active: link.active }">
<a
@ -9,8 +9,8 @@
ng-href="{{ link.active ? link.url : (link.lastSubUrl || link.url) }}"
data-test-subj="appLink">
<div ng-if="link.icon" ng-style="{ 'background-image': 'url(../' + link.icon + ')' }" class="app-icon"></div>
<div ng-if="!link.icon" class="app-icon app-icon-missing">{{ link.title[0] }}</div>
<div ng-if="link.icon" class="app-icon"><img ng-src="{{'../' + link.icon}}"></div>
<div ng-if="!link.icon" class="app-icon-missing">{{ link.title[0] }}</div>
<div class="app-title">{{ link.title }}</div>
</a>

View file

@ -1,4 +1,3 @@
import DomLocationProvider from 'ui/domLocation';
import { parse } from 'url';
import { bindKey } from 'lodash';
@ -8,14 +7,45 @@ import appSwitcherTemplate from './app_switcher.html';
uiModules
.get('kibana')
.provider('appSwitcherEnsureNavigation', function () {
let forceNavigation = false;
this.forceNavigation = function (val) {
forceNavigation = !!val;
};
this.$get = ['Private', function (Private) {
const domLocation = Private(DomLocationProvider);
return function (event) {
if (!forceNavigation || event.isDefaultPrevented() || event.altKey || event.metaKey || event.ctrlKey) {
return;
}
const toParsed = parse(event.delegateTarget.href);
const fromParsed = parse(domLocation.href);
const sameProto = toParsed.protocol === fromParsed.protocol;
const sameHost = toParsed.host === fromParsed.host;
const samePath = toParsed.path === fromParsed.path;
if (sameProto && sameHost && samePath) {
toParsed.hash && domLocation.reload();
// event.preventDefault() keeps the browser from seeing the new url as an update
// and even setting window.location does not mimic that behavior, so instead
// we use stopPropagation() to prevent angular from seeing the click and
// starting a digest cycle/attempting to handle it in the router.
event.stopPropagation();
}
};
}];
})
.directive('appSwitcher', function () {
return {
restrict: 'E',
template: appSwitcherTemplate,
controllerAs: 'switcher',
controller: function ($scope, Private) {
var domLocation = Private(DomLocationProvider);
controller($scope, appSwitcherEnsureNavigation) {
// since we render this in an isolate scope we can't "require: ^chrome", but
// rather than remove all helpfull checks we can just check here.
if (!$scope.chrome || !$scope.chrome.getNavLinks) {
@ -26,28 +56,7 @@ uiModules
// links don't cause full-navigation events in certain scenarios
// so we force them when needed
this.ensureNavigation = function (event, app) {
if (event.isDefaultPrevented() || event.altKey || event.metaKey || event.ctrlKey) {
return;
}
var toParsed = parse(event.delegateTarget.href);
var fromParsed = parse(domLocation.href);
var sameProto = toParsed.protocol === fromParsed.protocol;
var sameHost = toParsed.host === fromParsed.host;
var samePath = toParsed.path === fromParsed.path;
if (sameProto && sameHost && samePath) {
toParsed.hash && domLocation.reload();
// event.preventDefault() keeps the browser from seeing the new url as an update
// and even setting window.location does not mimic that behavior, so instead
// we use stopPropagation() to prevent angular from seeing the click and
// starting a digest cycle/attempting to handle it in the router.
event.stopPropagation();
}
};
this.ensureNavigation = appSwitcherEnsureNavigation;
}
};
});

View file

@ -2,11 +2,12 @@
@import (reference) "~ui/styles/variables";
// as - App Switcher
@as-open-width: 170px;
@as-open-width: 160px;
@as-closed-width: 53px;
@app-icon-height: 30px;
@transition-time: .65s;
@transition-delay: .2s;
@transition-time: .35s;
@transition-delay: .1s;
@app-links-wrapper-background: #3caed2;
body { overflow-x: hidden; }
@ -17,7 +18,7 @@ body { overflow-x: hidden; }
top: 0;
bottom: 0;
z-index: 0;
background-color: #3caed2;
background-color: @app-links-wrapper-background;
overflow: hidden;
transition: width @transition-time;
transition-delay: @transition-delay;
@ -36,7 +37,11 @@ body { overflow-x: hidden; }
width: @as-open-width;
list-style-type: none;
&.kibana {
background: url("~ui/images/kibana.svg") center / 160px 70px no-repeat #e8488b;
background-image: url("~ui/images/kibana.svg");
background-position: 6px 10px;
background-size: 140px 50px;
background-repeat: no-repeat;
background-color: #e8488b;
}
}
.bottom-apps {
@ -57,6 +62,11 @@ body { overflow-x: hidden; }
margin: 0 auto;
background-color: #fff;
&-panel {
.flex-parent(@shrink: 0);
box-shadow: -4px 0px 3px rgba(0,0,0,0.2);
}
.navbar-right {
margin-right: 0;
}
@ -67,12 +77,36 @@ body { overflow-x: hidden; }
.app-link {
width: @as-open-width;
height: @app-icon-height;
line-height: 24px;
> a {
display: block;
height: 100%;
color: #fff;
}
&:hover {
background-color: lighten(@app-links-wrapper-background, 7.5%);
}
.app-icon {
float: left;
filter: invert(100%);
font-weight: bold;
text-align: center;
font-size: 1.7em;
display: inline-block;
height: @app-icon-height;
width: @as-closed-width;
> i {
}
> img {
height: 20px;
}
}
.app-icon-missing {
float: left;
text-align: center;
font-size: 1.7em;
@ -81,8 +115,7 @@ body { overflow-x: hidden; }
width: @as-closed-width;
background-position: center;
background-size: contain;
> i {
}
background-repeat: no-repeat;
}
.app-title {
@ -90,16 +123,20 @@ body { overflow-x: hidden; }
display: inline-block;
float: right;
font-size: 0.9em;
text-align: center;
text-align: left;
padding-left: 3px;
line-height: @app-icon-height;
}
&.active {
background-color: #73c6e0;
background-color: lighten(@app-links-wrapper-background, 10%);
> a {
color: #333;
text-decoration: none;
}
img {
filter: invert(100%);
}
}
}

View file

@ -35,8 +35,8 @@ export default function (chrome, internals) {
const onRouteChange = function () {
let { href } = window.location;
let persist = chrome.getVisible();
internals.trackPossibleSubUrl(href);
internals.tabs.consumeRouteUpdate(href, persist);
internals.trackPossibleSubUrl(href);
};
$rootScope.$on('$routeChangeSuccess', onRouteChange);

View file

@ -1 +1,110 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 141.37 51"><defs><style>.cls-1,.cls-2,.cls-3,.cls-4{fill:#fff;}.cls-1{opacity:0.6;}.cls-2{opacity:0.4;}.cls-3{opacity:0.9;}</style></defs><title>Kibana-Full-Logo</title><path class="cls-1" d="M409.68,273.59a38.94,38.94,0,0,1,18.71,4.76L448.2,254.5H408.94v19.11Z" transform="translate(-408.94 -254.5)"/><path class="cls-1" d="M428.39,278.35l-19.45,23.42v3.73h39.2A39.2,39.2,0,0,0,428.39,278.35Z" transform="translate(-408.94 -254.5)"/><path class="cls-2" d="M428.39,278.35l-19.45,23.42v3.73h7l18.87-22.77s-1.25-1.06-3-2.3C430.41,279.46,428.39,278.35,428.39,278.35Z" transform="translate(-408.94 -254.5)"/><path class="cls-3" d="M409.68,273.59l-0.74,0v28.17l19.45-23.42A38.94,38.94,0,0,0,409.68,273.59Z" transform="translate(-408.94 -254.5)"/><path class="cls-4" d="M457.06,291.29H453.2V266.23h3.86v14.48l2.2-.21,4.18-6.9h4.32l-5.1,8.28,5.38,9.42h-4.36l-4.32-7.47-2.3.25v7.22Z" transform="translate(-408.94 -254.5)"/><path class="cls-4" d="M470.86,270.58v-4.07h3.86v4.07h-3.86Zm0,20.71v-17.7h3.86v17.7h-3.86Z" transform="translate(-408.94 -254.5)"/><path class="cls-4" d="M487.64,273.2q3.65,0,5.26,2t1.61,7.2q0,5.17-1.91,7.21t-6.73,2q-1.66,0-5.27-.32l-1.2-.11v-25h3.82v8A11.25,11.25,0,0,1,487.64,273.2Zm-1.77,15q2.83,0,3.77-1.33a8.11,8.11,0,0,0,.94-4.51,9.08,9.08,0,0,0-.8-4.48,2.82,2.82,0,0,0-2.6-1.29,11.33,11.33,0,0,0-3.4.53l-0.57.18v10.76Q485.17,288.25,485.87,288.25Z" transform="translate(-408.94 -254.5)"/><path class="cls-4" d="M511.47,279v8.07a1.68,1.68,0,0,0,.41,1.15,2,2,0,0,0,1.15.48l-0.11,2.94a7.45,7.45,0,0,1-4.71-1.31,13.47,13.47,0,0,1-5.7,1.31q-5.28,0-5.28-5.63a4.81,4.81,0,0,1,1.43-3.89,7.81,7.81,0,0,1,4.41-1.45l4.57-.39V279a2.15,2.15,0,0,0-2.44-2.55q-2.27,0-5.66.28l-1.13.07-0.14-2.73a30.77,30.77,0,0,1,7.1-.92q3.24,0,4.67,1.4A6,6,0,0,1,511.47,279Zm-7.93,4.39a2.38,2.38,0,0,0-2.44,2.66q0,2.44,2.16,2.44A13.67,13.67,0,0,0,507,288l0.64-.21V283Z" transform="translate(-408.94 -254.5)"/><path class="cls-4" d="M520.18,291.29h-3.86v-17.7h3.82v1.1A9.85,9.85,0,0,1,525,273.2q3.54,0,4.83,2t1.29,6.6v9.49h-3.82v-9.38a9.58,9.58,0,0,0-.62-4.07,2.57,2.57,0,0,0-2.53-1.2,8.77,8.77,0,0,0-3.47.71l-0.53.21v13.74Z" transform="translate(-408.94 -254.5)"/><path class="cls-4" d="M548.75,279v8.07a1.68,1.68,0,0,0,.41,1.15,2,2,0,0,0,1.15.48l-0.11,2.94a7.45,7.45,0,0,1-4.71-1.31,13.48,13.48,0,0,1-5.7,1.31q-5.28,0-5.28-5.63a4.82,4.82,0,0,1,1.43-3.89,7.81,7.81,0,0,1,4.41-1.45l4.57-.39V279a2.15,2.15,0,0,0-2.44-2.55q-2.27,0-5.66.28l-1.13.07-0.14-2.73a30.78,30.78,0,0,1,7.1-.92q3.24,0,4.67,1.4A6,6,0,0,1,548.75,279Zm-7.93,4.39a2.38,2.38,0,0,0-2.44,2.66q0,2.44,2.16,2.44a13.66,13.66,0,0,0,3.75-.57l0.64-.21V283Z" transform="translate(-408.94 -254.5)"/></svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="Layer_1"
data-name="Layer 1"
viewBox="0 0 141.37 51"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="kibana.svg">
<metadata
id="metadata31">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>Kibana-Full-Logo</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#515151"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:window-width="1796"
inkscape:window-height="1079"
id="namedview29"
showgrid="false"
inkscape:zoom="2.0725756"
inkscape:cx="70.684998"
inkscape:cy="25.5"
inkscape:window-x="349"
inkscape:window-y="210"
inkscape:window-maximized="0"
inkscape:current-layer="Layer_1" />
<defs
id="defs3">
<style
id="style5">.cls-1,.cls-2,.cls-3,.cls-4{fill:#fff;}.cls-1{opacity:0.6;}.cls-2{opacity:0.4;}.cls-3{opacity:0.9;}</style>
</defs>
<title
id="title7">Kibana-Full-Logo</title>
<path
class="cls-1"
d="M409.68,273.59a38.94,38.94,0,0,1,18.71,4.76L448.2,254.5H408.94v19.11Z"
transform="translate(-408.94 -254.5)"
id="path9" />
<path
class="cls-1"
d="M428.39,278.35l-19.45,23.42v3.73h39.2A39.2,39.2,0,0,0,428.39,278.35Z"
transform="translate(-408.94 -254.5)"
id="path11" />
<path
class="cls-2"
d="M428.39,278.35l-19.45,23.42v3.73h7l18.87-22.77s-1.25-1.06-3-2.3C430.41,279.46,428.39,278.35,428.39,278.35Z"
transform="translate(-408.94 -254.5)"
id="path13" />
<path
class="cls-3"
d="M409.68,273.59l-0.74,0v28.17l19.45-23.42A38.94,38.94,0,0,0,409.68,273.59Z"
transform="translate(-408.94 -254.5)"
id="path15" />
<g
style="font-style:normal;font-weight:normal;font-size:33.04906845px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:0.92528737;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="text4159"
transform="translate(0,-13.509761)">
<path
d="m 53.346838,50.53812 0,-6.74201 2.14819,-0.231343 4.031986,6.973353 4.065036,0 -5.023459,-8.791052 4.759066,-7.733482 -4.031986,0 -3.89979,6.444568 -2.049043,0.198295 0,-13.517069 -3.602348,0 0,23.39874 3.602348,0 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4891"
inkscape:connector-curvature="0" />
<path
d="m 66.269024,50.53812 3.602349,0 0,-16.524534 -3.602349,0 0,16.524534 z m 0,-19.333705 3.602349,0 0,-3.800643 -3.602349,0 0,3.800643 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4893"
inkscape:connector-curvature="0" />
<path
d="m 81.973529,33.650046 c -1.850747,0 -4.131133,0.991472 -4.131133,0.991472 l 0,-7.502138 -3.5693,0 0,23.365691 c 0,0 4.296379,0.396589 6.04798,0.396589 5.915783,0 8.063973,-2.015993 8.063973,-8.85715 0,-6.180176 -1.916846,-8.394464 -6.41152,-8.394464 z M 80.321076,47.6959 c -0.660981,0 -2.47868,-0.132196 -2.47868,-0.132196 l 0,-10.046917 c 0,0 1.949895,-0.660981 3.701495,-0.660981 2.214288,0 3.172711,1.454159 3.172711,5.188704 0,4.031986 -0.727079,5.65139 -4.395526,5.65139 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4895"
inkscape:connector-curvature="0" />
<path
d="m 104.18766,39.103143 c 0,-3.767594 -1.65245,-5.453097 -5.684435,-5.453097 -3.040515,0 -6.642863,0.859276 -6.642863,0.859276 l 0.132196,2.544778 c 0,0 3.998938,-0.33049 6.213225,-0.33049 1.619405,0 2.412587,0.561834 2.412587,2.379533 l 0,1.189766 -4.263335,0.36354 c -3.53625,0.297441 -5.453096,1.487208 -5.453096,4.990409 0,3.437103 1.652453,5.254802 4.924311,5.254802 2.676975,0 5.3209,-1.222816 5.3209,-1.222816 1.22282,0.958423 2.37953,1.222816 4.39553,1.222816 l 0.0991,-2.743073 c -0.95842,-0.132196 -1.38806,-0.528785 -1.45416,-1.520257 l 0,-7.535187 z m -3.56929,3.734544 0,4.395526 c 0,0 -2.214292,0.72708 -4.098089,0.72708 -1.388061,0 -2.015993,-0.925374 -2.015993,-2.412582 0,-1.487208 0.760128,-2.214288 2.280385,-2.346484 l 3.833697,-0.36354 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4897"
inkscape:connector-curvature="0" />
<path
d="m 112.28004,50.53812 0,-12.823038 c 0,0 1.8177,-0.859276 3.73454,-0.859276 2.51173,0 2.94137,1.619404 2.94137,4.924311 l 0,8.758003 3.5693,0 0,-8.85715 c 0,-5.420047 -1.12367,-8.030924 -5.71749,-8.030924 -2.14819,0 -4.56077,1.388061 -4.56077,1.388061 l 0,-1.024521 -3.5693,0 0,16.524534 3.60235,0 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4899"
inkscape:connector-curvature="0" />
<path
d="m 138.91501,39.103143 c 0,-3.767594 -1.65246,-5.453097 -5.68444,-5.453097 -3.04052,0 -6.64287,0.859276 -6.64287,0.859276 l 0.1322,2.544778 c 0,0 3.99894,-0.33049 6.21322,-0.33049 1.61941,0 2.41259,0.561834 2.41259,2.379533 l 0,1.189766 -4.26333,0.36354 c -3.53625,0.297441 -5.4531,1.487208 -5.4531,4.990409 0,3.437103 1.65245,5.254802 4.92431,5.254802 2.67698,0 5.3209,-1.222816 5.3209,-1.222816 1.22282,0.958423 2.37953,1.222816 4.39553,1.222816 l 0.0991,-2.743073 c -0.95842,-0.132196 -1.38806,-0.528785 -1.45415,-1.520257 l 0,-7.535187 z m -3.5693,3.734544 0,4.395526 c 0,0 -2.21429,0.72708 -4.09809,0.72708 -1.38806,0 -2.01599,-0.925374 -2.01599,-2.412582 0,-1.487208 0.76013,-2.214288 2.28038,-2.346484 l 3.8337,-0.36354 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
id="path4901"
inkscape:connector-curvature="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Before After
Before After

View file

@ -1,5 +1,4 @@
import _ from 'lodash';
import { join } from 'path';
import { chain, get, noop, once, pick } from 'lodash';
class UiApp {
constructor(uiExports, spec) {
@ -8,6 +7,7 @@ class UiApp {
this.id = this.spec.id;
if (!this.id) {
console.log('-----------Spec Without ID:', this.spec);
throw new Error('Every app must specify it\'s id');
}
@ -15,9 +15,24 @@ class UiApp {
this.title = this.spec.title;
this.description = this.spec.description;
this.icon = this.spec.icon;
this.hidden = this.spec.hidden;
this.hidden = !!this.spec.hidden;
this.listed = this.spec.listed == null ? !this.hidden : this.spec.listed;
this.templateName = this.spec.templateName || 'ui_app';
this.url = `${spec.urlBasePath || ''}${this.spec.url || `/app/${this.id}`}`;
if (!this.hidden) {
// any non-hidden app has a url, so it gets a "navLink"
this.navLink = this.uiExports.navLinks.new({
title: this.title,
description: this.description,
icon: this.icon,
url: this.spec.url || `/app/${this.id}`
});
if (!this.listed) {
// unlisted apps remove their navLinks from the uiExports collection though
this.uiExports.navLinks.delete(this.navLink);
}
}
if (this.spec.autoload) {
console.warn(
@ -27,15 +42,15 @@ class UiApp {
}
// once this resolves, no reason to run it again
this.getModules = _.once(this.getModules);
this.getModules = once(this.getModules);
// variables that are injected into the browser, must serialize to JSON
this.getInjectedVars = this.spec.injectVars || _.noop;
this.getInjectedVars = this.spec.injectVars || noop;
}
getModules() {
return _.chain([
this.uiExports.find(_.get(this, 'spec.uses', [])),
return chain([
this.uiExports.find(get(this, 'spec.uses', [])),
this.uiExports.find(['chromeNavControls', 'sledgehammers']),
])
.flatten()
@ -45,7 +60,7 @@ class UiApp {
}
toJSON() {
return _.pick(this, ['id', 'title', 'description', 'icon', 'main', 'url']);
return pick(this, ['id', 'title', 'description', 'icon', 'main', 'navLink']);
}
}

View file

@ -21,6 +21,7 @@ module.exports = class UiAppCollection extends Collection {
}
new(spec) {
console.log('--------NewAppSpec:', spec);
if (this.hidden && spec.hidden) {
return this.hidden.new(spec);
}

View file

@ -2,9 +2,11 @@ import _ from 'lodash';
import minimatch from 'minimatch';
import UiAppCollection from './ui_app_collection';
import UiNavLinkCollection from './ui_nav_link_collection';
class UiExports {
constructor({ urlBasePath }) {
this.navLinks = new UiNavLinkCollection(this);
this.apps = new UiAppCollection(this);
this.aliases = {};
this.urlBasePath = urlBasePath;
@ -48,15 +50,21 @@ class UiExports {
case 'app':
case 'apps':
return (plugin, specs) => {
const id = plugin.id;
for (let spec of [].concat(specs || [])) {
let app = this.apps.new(_.defaults({}, spec, {
id: plugin.id,
urlBasePath: this.urlBasePath
}));
const app = this.apps.new({ id, ...spec });
plugin.apps.add(app);
}
};
case 'link':
case 'links':
return (plugin, spec) => {
for (const spec of [].concat(spec || [])) {
this.navLinks.new(spec);
}
};
case 'visTypes':
case 'fieldFormats':
case 'spyModes':

16
src/ui/ui_nav_link.js Normal file
View file

@ -0,0 +1,16 @@
import { pick } from 'lodash';
import { join } from 'path';
export default class UiNavLink {
constructor(uiExports, spec) {
this.title = spec.title;
this.order = spec.order || 0;
this.url = `${uiExports.urlBasePath || ''}${spec.url}`;
this.description = spec.description;
this.icon = spec.icon;
}
toJSON() {
return pick(this, ['title', 'url', 'order', 'description', 'icon']);
}
}

View file

@ -0,0 +1,29 @@
import { sortBy } from 'lodash';
import UiNavLink from './ui_nav_link';
import Collection from '../utils/Collection';
const inOrderCache = Symbol('inOrder');
export default class UiNavLinkCollection extends Collection {
constructor(uiExports, parent) {
super();
this.uiExports = uiExports;
}
new(spec) {
const link = new UiNavLink(this.uiExports, spec);
this[inOrderCache] = null;
this.add(link);
return link;
}
get inOrder() {
if (!this[inOrderCache]) {
this[inOrderCache] = sortBy([...this], 'order');
}
return this[inOrderCache];
}
};

View file

@ -5,6 +5,7 @@ import { includes } from 'lodash';
export default function filesToCommit(path) {
const simpleGit = new SimpleGit(path);
const gitDiff = promisify(simpleGit.diff, simpleGit);
const pathsToIgnore = ['webpackShims'];
return gitDiff(['--name-status', '--cached'])
.then(output => {
@ -12,6 +13,9 @@ export default function filesToCommit(path) {
.split('\n')
.map(line => line.trim().split('\t'))
.filter(parts => parts.length === 2)
.filter(parts => {
return pathsToIgnore.reduce((prev, curr) => {(prev === false) ? prev : parts[1].indexOf(curr) === -1;}, true);
})
.map(parts => {
const status = parts.shift();
const name = parts.join('\t').trim();

View file

@ -154,11 +154,11 @@ define(function (require) {
var self = this;
return self.remote.setFindTimeout(defaultTimeout)
.findByCssSelector('.app-wrapper > .application')
.findByCssSelector('.app-wrapper .application')
.then(function () {
return self.runScript(function () {
var $ = window.$;
var $scope = $('.app-wrapper > .application').scope();
var $scope = $('.app-wrapper .application').scope();
return $scope ? $scope.chrome.getApp() : {};
});
});