diff --git a/config/kibana.yml b/config/kibana.yml
index be96fdc55187b..db9e0d60f9fc2 100644
--- a/config/kibana.yml
+++ b/config/kibana.yml
@@ -30,7 +30,7 @@
#kibana.index: ".kibana"
# The default application to load.
-#kibana.defaultAppId: "discover"
+#kibana.defaultAppId: "home"
# If your Elasticsearch is protected with basic authentication, these settings provide
# the username and password that the Kibana server uses to perform maintenance on the Kibana
diff --git a/package.json b/package.json
index 9d8df96a3d0d8..b963792fbebbd 100644
--- a/package.json
+++ b/package.json
@@ -177,9 +177,10 @@
"react-input-autosize": "1.1.0",
"react-input-range": "1.2.1",
"react-markdown": "2.4.2",
- "react-redux": "4.4.5",
+ "react-redux": "5.0.5",
"react-router": "2.0.0",
"react-router-redux": "4.0.4",
+ "react-router-dom": "4.2.2",
"react-select": "1.0.0-rc.5",
"react-sizeme": "2.3.4",
"react-sortable": "1.1.0",
diff --git a/src/core_plugins/kibana/index.js b/src/core_plugins/kibana/index.js
index 58f0320f89991..2ce1b2624b0cc 100644
--- a/src/core_plugins/kibana/index.js
+++ b/src/core_plugins/kibana/index.js
@@ -27,7 +27,7 @@ export default function (kibana) {
config: function (Joi) {
return Joi.object({
enabled: Joi.boolean().default(true),
- defaultAppId: Joi.string().default('discover'),
+ defaultAppId: Joi.string().default('home'),
index: Joi.string().default('.kibana')
}).default();
},
@@ -47,6 +47,7 @@ export default function (kibana) {
description: 'the kibana you know and love',
main: 'plugins/kibana/kibana',
uses: [
+ 'home',
'visTypes',
'visResponseHandlers',
'visRequestHandlers',
diff --git a/src/core_plugins/kibana/public/assets/app_apm.svg b/src/core_plugins/kibana/public/assets/app_apm.svg
new file mode 100644
index 0000000000000..3690af6a531d4
--- /dev/null
+++ b/src/core_plugins/kibana/public/assets/app_apm.svg
@@ -0,0 +1,30 @@
+
diff --git a/src/core_plugins/kibana/public/assets/app_dashboard.svg b/src/core_plugins/kibana/public/assets/app_dashboard.svg
new file mode 100644
index 0000000000000..b57896fea6ef2
--- /dev/null
+++ b/src/core_plugins/kibana/public/assets/app_dashboard.svg
@@ -0,0 +1,8 @@
+
diff --git a/src/core_plugins/kibana/public/assets/app_devtools.svg b/src/core_plugins/kibana/public/assets/app_devtools.svg
new file mode 100644
index 0000000000000..9c997ac34a462
--- /dev/null
+++ b/src/core_plugins/kibana/public/assets/app_devtools.svg
@@ -0,0 +1,7 @@
+
diff --git a/src/core_plugins/kibana/public/assets/app_discover.svg b/src/core_plugins/kibana/public/assets/app_discover.svg
new file mode 100644
index 0000000000000..4193416991dd6
--- /dev/null
+++ b/src/core_plugins/kibana/public/assets/app_discover.svg
@@ -0,0 +1,8 @@
+
diff --git a/src/core_plugins/kibana/public/assets/app_logging.svg b/src/core_plugins/kibana/public/assets/app_logging.svg
new file mode 100644
index 0000000000000..de0e4bac99d63
--- /dev/null
+++ b/src/core_plugins/kibana/public/assets/app_logging.svg
@@ -0,0 +1,11 @@
+
diff --git a/src/core_plugins/kibana/public/assets/app_monitoring.svg b/src/core_plugins/kibana/public/assets/app_monitoring.svg
new file mode 100644
index 0000000000000..47c96d4f26e77
--- /dev/null
+++ b/src/core_plugins/kibana/public/assets/app_monitoring.svg
@@ -0,0 +1,7 @@
+
diff --git a/src/core_plugins/kibana/public/assets/app_security.svg b/src/core_plugins/kibana/public/assets/app_security.svg
new file mode 100644
index 0000000000000..95b1903843434
--- /dev/null
+++ b/src/core_plugins/kibana/public/assets/app_security.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/core_plugins/kibana/public/assets/app_timelion.svg b/src/core_plugins/kibana/public/assets/app_timelion.svg
new file mode 100644
index 0000000000000..c470e746bb8c1
--- /dev/null
+++ b/src/core_plugins/kibana/public/assets/app_timelion.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/core_plugins/kibana/public/assets/app_visualize.svg b/src/core_plugins/kibana/public/assets/app_visualize.svg
new file mode 100644
index 0000000000000..cd2bfe5f15acc
--- /dev/null
+++ b/src/core_plugins/kibana/public/assets/app_visualize.svg
@@ -0,0 +1,7 @@
+
diff --git a/src/core_plugins/kibana/public/dashboard/index.js b/src/core_plugins/kibana/public/dashboard/index.js
index 160d76ab71cf5..cc8036c96046e 100644
--- a/src/core_plugins/kibana/public/dashboard/index.js
+++ b/src/core_plugins/kibana/public/dashboard/index.js
@@ -11,6 +11,7 @@ import dashboardListingTemplate from './listing/dashboard_listing.html';
import { DashboardListingController } from './listing/dashboard_listing';
import { DashboardConstants, createDashboardEditUrl } from './dashboard_constants';
import { SavedObjectNotFound } from 'ui/errors';
+import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue';
uiRoutes
.defaults(/dashboard/, {
@@ -56,3 +57,15 @@ uiRoutes
}
}
});
+
+FeatureCatalogueRegistryProvider.register(() => {
+ return {
+ id: 'dashboard',
+ title: 'Dashboards',
+ description: 'Create visual landing pages made up of content from other apps.',
+ icon: '/plugins/kibana/assets/app_dashboard.svg',
+ path: `/app/kibana#${DashboardConstants.LANDING_PAGE_PATH}`,
+ showOnHomePage: true,
+ category: FeatureCatalogueCategory.DATA
+ };
+});
diff --git a/src/core_plugins/kibana/public/dev_tools/index.js b/src/core_plugins/kibana/public/dev_tools/index.js
index 91a37f49e7e5d..7b34750830f93 100644
--- a/src/core_plugins/kibana/public/dev_tools/index.js
+++ b/src/core_plugins/kibana/public/dev_tools/index.js
@@ -1,5 +1,6 @@
import uiRoutes from 'ui/routes';
import { DevToolsRegistryProvider } from 'ui/registry/dev_tools';
+import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue';
import 'plugins/kibana/dev_tools/directives/dev_tools_app';
uiRoutes
@@ -11,3 +12,15 @@ uiRoutes
}
}
});
+
+FeatureCatalogueRegistryProvider.register(() => {
+ return {
+ id: 'console',
+ title: 'Console',
+ description: 'Manipulate your ES data directly with console.',
+ icon: '/plugins/kibana/assets/app_devtools.svg',
+ path: '/app/kibana#/dev_tools/console',
+ showOnHomePage: true,
+ category: FeatureCatalogueCategory.ADMIN
+ };
+});
diff --git a/src/core_plugins/kibana/public/discover/index.js b/src/core_plugins/kibana/public/discover/index.js
index 29d7bbd5c311f..d6f46d3c24005 100644
--- a/src/core_plugins/kibana/public/discover/index.js
+++ b/src/core_plugins/kibana/public/discover/index.js
@@ -6,3 +6,16 @@ import 'plugins/kibana/discover/components/field_chooser/field_chooser';
import 'plugins/kibana/discover/controllers/discover';
import 'plugins/kibana/discover/styles/main.less';
import 'ui/doc_table/components/table_row';
+import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue';
+
+FeatureCatalogueRegistryProvider.register(() => {
+ return {
+ id: 'discover',
+ title: 'Discover',
+ description: 'Search and explore your data.',
+ icon: '/plugins/kibana/assets/app_discover.svg',
+ path: '/app/kibana#/discover',
+ showOnHomePage: true,
+ category: FeatureCatalogueCategory.DATA
+ };
+});
diff --git a/src/core_plugins/kibana/public/home/components/feature_directory.js b/src/core_plugins/kibana/public/home/components/feature_directory.js
new file mode 100644
index 0000000000000..13bb301092df9
--- /dev/null
+++ b/src/core_plugins/kibana/public/home/components/feature_directory.js
@@ -0,0 +1,124 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Synopsis } from './synopsis';
+import {
+ KuiTabs,
+ KuiTab,
+ KuiFlexItem,
+ KuiFlexGrid,
+} from 'ui_framework/components';
+import { FeatureCatalogueCategory } from 'ui/registry/feature_catalogue';
+
+const ALL_TAB_ID = 'all';
+const OTHERS_TAB_ID = 'others';
+
+const isOtherCategory = (directory) => {
+ return directory.category !== FeatureCatalogueCategory.DATA && directory.category !== FeatureCatalogueCategory.ADMIN;
+};
+
+export class FeatureDirectory extends React.Component {
+
+ constructor(props) {
+ super(props);
+
+ this.tabs = [{
+ id: ALL_TAB_ID,
+ name: 'All',
+ }, {
+ id: FeatureCatalogueCategory.DATA,
+ name: 'Explore & Visualize',
+ }, {
+ id: FeatureCatalogueCategory.ADMIN,
+ name: 'Administrative',
+ }];
+ if (props.directories.some(isOtherCategory)) {
+ this.tabs.push({
+ id: OTHERS_TAB_ID,
+ name: 'Other',
+ });
+ }
+
+ this.state = {
+ selectedTabId: ALL_TAB_ID
+ };
+ }
+
+ onSelectedTabChanged = id => {
+ this.setState({
+ selectedTabId: id,
+ });
+ };
+
+ renderTabs = () => {
+ return this.tabs.map((tab, index) => (
+ this.onSelectedTabChanged(tab.id)}
+ isSelected={tab.id === this.state.selectedTabId}
+ key={index}
+ >
+ {tab.name}
+
+ ));
+ }
+
+ renderDirectories = () => {
+ return this.props.directories
+ .filter((directory) => {
+ if (this.state.selectedTabId === ALL_TAB_ID) {
+ return true;
+ }
+ if (this.state.selectedTabId === OTHERS_TAB_ID) {
+ return isOtherCategory(directory);
+ }
+ return this.state.selectedTabId === directory.category;
+ })
+ .map((directory) => {
+ return (
+
+
+
+ );
+ });
+ };
+
+ render() {
+ return (
+
+
+
+
+ Feature Directory
+
+
+
+
+ {this.renderTabs()}
+
+
+ { this.renderDirectories() }
+
+
+
+
+ );
+ }
+}
+
+FeatureDirectory.propTypes = {
+ addBasePath: PropTypes.func.isRequired,
+ directories: PropTypes.arrayOf(PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ title: PropTypes.string.isRequired,
+ description: PropTypes.string.isRequired,
+ icon: PropTypes.string.isRequired,
+ path: PropTypes.string.isRequired,
+ showOnHomePage: PropTypes.bool.isRequired,
+ category: PropTypes.string.isRequired
+ }))
+};
diff --git a/src/core_plugins/kibana/public/home/components/home.js b/src/core_plugins/kibana/public/home/components/home.js
new file mode 100644
index 0000000000000..22fb7c50de0ad
--- /dev/null
+++ b/src/core_plugins/kibana/public/home/components/home.js
@@ -0,0 +1,121 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Synopsis } from './synopsis';
+import {
+ KuiLinkButton,
+ KuiFlexGroup,
+ KuiFlexItem,
+ KuiFlexGrid,
+} from 'ui_framework/components';
+import { FeatureCatalogueCategory } from 'ui/registry/feature_catalogue';
+
+export function Home({ addBasePath, directories }) {
+
+ const renderDirectories = (category) => {
+ return directories
+ .filter((directory) => {
+ return directory.showOnHomePage && directory.category === category;
+ })
+ .map((directory) => {
+ return (
+
+
+
+ );
+ });
+ };
+
+
+ return (
+
+
+
+
+
+
+
+ Welcome to Kibana
+
+
+
+
+
+
+
+ Data already in Elasticsearch?
+
+
+
+
+
+ Set up index patterns
+
+
+
+
+
+
+
+
+
+
+
+
+ Visualize and explore data
+
+
+ { renderDirectories(FeatureCatalogueCategory.DATA) }
+
+
+
+
+ Manage and administer the Elastic stack
+
+
+ { renderDirectories(FeatureCatalogueCategory.ADMIN) }
+
+
+
+
+
+
+
+ {`Didn't find what you were looking for?`}
+
+
+ View full directory of Kibana features
+
+
+
+
+
+ );
+}
+
+Home.propTypes = {
+ addBasePath: PropTypes.func.isRequired,
+ directories: PropTypes.arrayOf(PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ title: PropTypes.string.isRequired,
+ description: PropTypes.string.isRequired,
+ icon: PropTypes.string.isRequired,
+ path: PropTypes.string.isRequired,
+ showOnHomePage: PropTypes.bool.isRequired,
+ category: PropTypes.string.isRequired
+ }))
+};
diff --git a/src/core_plugins/kibana/public/home/components/home_app.js b/src/core_plugins/kibana/public/home/components/home_app.js
new file mode 100644
index 0000000000000..19f61bcc887ed
--- /dev/null
+++ b/src/core_plugins/kibana/public/home/components/home_app.js
@@ -0,0 +1,47 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Home } from './home';
+import { FeatureDirectory } from './feature_directory';
+import {
+ HashRouter as Router,
+ Switch,
+ Route
+} from 'react-router-dom';
+
+export function HomeApp({ addBasePath, directories }) {
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+HomeApp.propTypes = {
+ addBasePath: PropTypes.func.isRequired,
+ directories: PropTypes.arrayOf(PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ title: PropTypes.string.isRequired,
+ description: PropTypes.string.isRequired,
+ icon: PropTypes.string.isRequired,
+ path: PropTypes.string.isRequired,
+ showOnHomePage: PropTypes.bool.isRequired,
+ category: PropTypes.string.isRequired
+ }))
+};
diff --git a/src/core_plugins/kibana/public/home/components/synopsis.js b/src/core_plugins/kibana/public/home/components/synopsis.js
new file mode 100644
index 0000000000000..396bf712a4b7e
--- /dev/null
+++ b/src/core_plugins/kibana/public/home/components/synopsis.js
@@ -0,0 +1,43 @@
+import './synopsis.less';
+import React from 'react';
+import PropTypes from 'prop-types';
+import {
+ KuiFlexGroup,
+ KuiFlexItem
+} from 'ui_framework/components';
+
+export function Synopsis({ description, iconUrl, title, url }) {
+ let img;
+ if (iconUrl) {
+ img = (
+
+ );
+ }
+
+ return (
+
+ {img}
+
+
+
+ {description}
+
+
+
+ );
+}
+
+Synopsis.propTypes = {
+ description: PropTypes.string.isRequired,
+ iconUrl: PropTypes.string,
+ title: PropTypes.string.isRequired,
+ url: PropTypes.string.isRequired
+};
diff --git a/src/core_plugins/kibana/public/home/components/synopsis.less b/src/core_plugins/kibana/public/home/components/synopsis.less
new file mode 100644
index 0000000000000..cca15340238ec
--- /dev/null
+++ b/src/core_plugins/kibana/public/home/components/synopsis.less
@@ -0,0 +1,12 @@
+.synopsis {
+ margin-left: 10px !important;
+}
+
+.synopsisTitle {
+ font-size: 16px;
+ font-weight: normal;
+}
+
+.synopsisIcon {
+ padding-top: 8px;
+}
diff --git a/src/core_plugins/kibana/public/home/home.less b/src/core_plugins/kibana/public/home/home.less
new file mode 100644
index 0000000000000..590a89ae74e21
--- /dev/null
+++ b/src/core_plugins/kibana/public/home/home.less
@@ -0,0 +1,28 @@
+@import (reference) "~ui/styles/variables.less";
+
+.home {
+ background-color: @globalColorLightestGray;
+ min-height: 100vh;
+}
+
+.homePanel {
+ padding: 24px;
+ background-color: @white;
+}
+
+.homeTopFeatures {
+ margin-top: 12px;
+}
+
+.homeFeatureDirectory {
+ background: @white;
+ margin: 0;
+ border-left: 1px solid @globalColorLightGray;
+ border-right: 1px solid @globalColorLightGray;
+ border-bottom: 1px solid @globalColorLightGray;
+ padding: 16px;
+}
+
+.homeFeatureCategoryTab {
+ background-color: @globalColorLightestGray;
+}
diff --git a/src/core_plugins/kibana/public/home/home_ng_wrapper.html b/src/core_plugins/kibana/public/home/home_ng_wrapper.html
new file mode 100644
index 0000000000000..5a13b1b527717
--- /dev/null
+++ b/src/core_plugins/kibana/public/home/home_ng_wrapper.html
@@ -0,0 +1,4 @@
+
diff --git a/src/core_plugins/kibana/public/home/index.js b/src/core_plugins/kibana/public/home/index.js
new file mode 100644
index 0000000000000..3098ccd508be1
--- /dev/null
+++ b/src/core_plugins/kibana/public/home/index.js
@@ -0,0 +1,29 @@
+import './home.less';
+import chrome from 'ui/chrome';
+import routes from 'ui/routes';
+import template from './home_ng_wrapper.html';
+import { FeatureCatalogueRegistryProvider } from 'ui/registry/feature_catalogue';
+import { uiModules } from 'ui/modules';
+import {
+ HomeApp
+} from './components/home_app';
+
+const app = uiModules.get('apps/home', []);
+app.directive('homeApp', function (reactDirective) {
+ return reactDirective(HomeApp);
+});
+
+function getRoute() {
+ return {
+ template,
+ controller($scope, Private) {
+ $scope.addBasePath = chrome.addBasePath;
+ $scope.directories = Private(FeatureCatalogueRegistryProvider).inTitleOrder;
+ }
+ };
+}
+
+// All routing will be handled inside HomeApp via react, we just need to make sure angular doesn't
+// redirect us to the default page by encountering a url it isn't marked as being able to handle.
+routes.when('/home', getRoute());
+routes.when('/home/feature_directory', getRoute());
diff --git a/src/core_plugins/kibana/public/kibana.js b/src/core_plugins/kibana/public/kibana.js
index 8c63785fa680f..3247aadcfc6ce 100644
--- a/src/core_plugins/kibana/public/kibana.js
+++ b/src/core_plugins/kibana/public/kibana.js
@@ -6,6 +6,7 @@ import routes from 'ui/routes';
import { uiModules } from 'ui/modules';
import 'ui/autoload/all';
+import 'plugins/kibana/home/index';
import 'plugins/kibana/discover/index';
import 'plugins/kibana/visualize/index';
import 'plugins/kibana/dashboard/index';
diff --git a/src/core_plugins/kibana/public/management/sections/indices/index.js b/src/core_plugins/kibana/public/management/sections/indices/index.js
index 8c04db6774ef9..a08f5332f3adf 100644
--- a/src/core_plugins/kibana/public/management/sections/indices/index.js
+++ b/src/core_plugins/kibana/public/management/sections/indices/index.js
@@ -5,6 +5,7 @@ import uiRoutes from 'ui/routes';
import { uiModules } from 'ui/modules';
import indexTemplate from 'plugins/kibana/management/sections/indices/index.html';
import { SavedObjectsClientProvider } from 'ui/saved_objects';
+import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue';
const indexPatternsResolutions = {
indexPatterns: function (Private) {
@@ -64,3 +65,15 @@ management.getSection('kibana').register('indices', {
order: 0,
url: '#/management/kibana/indices/'
});
+
+FeatureCatalogueRegistryProvider.register(() => {
+ return {
+ id: 'index_patterns',
+ title: 'Index Patterns',
+ description: 'Make your ES data usable from within kibana.',
+ icon: '/plugins/kibana/assets/app_dashboard.svg',
+ path: '/app/kibana#/management/kibana/indices',
+ showOnHomePage: true,
+ category: FeatureCatalogueCategory.ADMIN
+ };
+});
diff --git a/src/core_plugins/kibana/public/management/sections/objects/index.js b/src/core_plugins/kibana/public/management/sections/objects/index.js
index 8edc4df15971c..83d5ba94ac311 100644
--- a/src/core_plugins/kibana/public/management/sections/objects/index.js
+++ b/src/core_plugins/kibana/public/management/sections/objects/index.js
@@ -4,6 +4,7 @@ import 'plugins/kibana/management/sections/objects/_objects';
import 'ace';
import 'ui/directives/confirm_click';
import { uiModules } from 'ui/modules';
+import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue';
// add the module deps to this module
uiModules.get('apps/management');
@@ -13,3 +14,15 @@ management.getSection('kibana').register('objects', {
order: 10,
url: '#/management/kibana/objects'
});
+
+FeatureCatalogueRegistryProvider.register(() => {
+ return {
+ id: 'saved_objects',
+ title: 'Saved objects',
+ description: 'Import / export your kibana objects for later reuse.',
+ icon: '/plugins/kibana/assets/app_dashboard.svg',
+ path: '/app/kibana#/management/kibana/objects',
+ showOnHomePage: true,
+ category: FeatureCatalogueCategory.ADMIN
+ };
+});
diff --git a/src/core_plugins/kibana/public/management/sections/settings/index.js b/src/core_plugins/kibana/public/management/sections/settings/index.js
index ddc721841a874..ecada7d2eba6e 100644
--- a/src/core_plugins/kibana/public/management/sections/settings/index.js
+++ b/src/core_plugins/kibana/public/management/sections/settings/index.js
@@ -5,6 +5,7 @@ import { management } from 'ui/management';
import uiRoutes from 'ui/routes';
import { uiModules } from 'ui/modules';
import indexTemplate from 'plugins/kibana/management/sections/settings/index.html';
+import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue';
uiRoutes
.when('/management/kibana/settings', {
@@ -44,3 +45,15 @@ management.getSection('kibana').register('settings', {
order: 20,
url: '#/management/kibana/settings'
});
+
+FeatureCatalogueRegistryProvider.register(() => {
+ return {
+ id: 'advanced_settings',
+ title: 'Advanced settings',
+ description: 'Use to customize your kibana instance.',
+ icon: '/plugins/kibana/assets/app_dashboard.svg',
+ path: '/app/kibana#/management/kibana/settings',
+ showOnHomePage: false,
+ category: FeatureCatalogueCategory.ADMIN
+ };
+});
diff --git a/src/core_plugins/kibana/public/visualize/index.js b/src/core_plugins/kibana/public/visualize/index.js
index f28886094d36a..3b25bd7d7fae1 100644
--- a/src/core_plugins/kibana/public/visualize/index.js
+++ b/src/core_plugins/kibana/public/visualize/index.js
@@ -13,6 +13,7 @@ import uiRoutes from 'ui/routes';
import visualizeListingTemplate from './listing/visualize_listing.html';
import { VisualizeListingController } from './listing/visualize_listing';
import { VisualizeConstants } from './visualize_constants';
+import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue';
uiRoutes
.defaults(/visualize/, {
@@ -24,3 +25,14 @@ uiRoutes
controllerAs: 'listingController',
});
+FeatureCatalogueRegistryProvider.register(() => {
+ return {
+ id: 'visualize',
+ title: 'Visualize',
+ description: 'Build a variety of graphs for your data.',
+ icon: '/plugins/kibana/assets/app_visualize.svg',
+ path: `/app/kibana#${VisualizeConstants.LANDING_PAGE_PATH}`,
+ showOnHomePage: true,
+ category: FeatureCatalogueCategory.DATA
+ };
+});
diff --git a/src/core_plugins/timelion/index.js b/src/core_plugins/timelion/index.js
index 93cb3a58b21bd..0b4724997c16f 100644
--- a/src/core_plugins/timelion/index.js
+++ b/src/core_plugins/timelion/index.js
@@ -1,4 +1,3 @@
-
export default function (kibana) {
let mainFile = 'plugins/timelion/app';
@@ -29,7 +28,7 @@ export default function (kibana) {
};
},
uses: [
- 'savedObjectTypes',
+ 'savedObjectTypes'
]
},
hacks: [
@@ -39,6 +38,9 @@ export default function (kibana) {
visTypes: [
'plugins/timelion/vis'
],
+ home: [
+ 'plugins/timelion/register_feature'
+ ],
mappings: require('./mappings.json'),
uiSettingDefaults: {
diff --git a/src/core_plugins/timelion/public/register_feature.js b/src/core_plugins/timelion/public/register_feature.js
new file mode 100644
index 0000000000000..c24cff534f148
--- /dev/null
+++ b/src/core_plugins/timelion/public/register_feature.js
@@ -0,0 +1,13 @@
+import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue';
+
+FeatureCatalogueRegistryProvider.register(() => {
+ return {
+ id: 'timelion',
+ title: 'Timelion',
+ description: 'Build powerful time based visualizations thru expressions.',
+ icon: '/plugins/kibana/assets/app_timelion.svg',
+ path: '/app/timelion',
+ showOnHomePage: true,
+ category: FeatureCatalogueCategory.DATA
+ };
+});
diff --git a/src/ui/public/chrome/directives/global_nav/global_nav.html b/src/ui/public/chrome/directives/global_nav/global_nav.html
index b149c8903d60b..828ccd9d7a191 100644
--- a/src/ui/public/chrome/directives/global_nav/global_nav.html
+++ b/src/ui/public/chrome/directives/global_nav/global_nav.html
@@ -6,25 +6,27 @@
diff --git a/src/ui/public/chrome/directives/global_nav/global_nav.js b/src/ui/public/chrome/directives/global_nav/global_nav.js
index 7b36d825059a6..8c20dee5b3ca4 100644
--- a/src/ui/public/chrome/directives/global_nav/global_nav.js
+++ b/src/ui/public/chrome/directives/global_nav/global_nav.js
@@ -8,7 +8,7 @@ import { uiModules } from 'ui/modules';
const module = uiModules.get('kibana');
-module.directive('globalNav', globalNavState => {
+module.directive('globalNav', (globalNavState, chrome) => {
return {
restrict: 'E',
replace: true,
@@ -41,6 +41,10 @@ module.directive('globalNav', globalNavState => {
updateGlobalNav();
});
+ scope.getHref = path => {
+ return chrome.addBasePath(path);
+ };
+
scope.toggleGlobalNav = event => {
event.preventDefault();
globalNavState.setOpen(!globalNavState.isOpen());
diff --git a/src/ui/public/registry/feature_catalogue.js b/src/ui/public/registry/feature_catalogue.js
new file mode 100644
index 0000000000000..8d5aa5bf17458
--- /dev/null
+++ b/src/ui/public/registry/feature_catalogue.js
@@ -0,0 +1,14 @@
+import { uiRegistry } from 'ui/registry/_registry';
+
+export const FeatureCatalogueRegistryProvider = uiRegistry({
+ name: 'featureCatalogue',
+ index: ['id'],
+ group: ['category'],
+ order: ['title']
+});
+
+export const FeatureCatalogueCategory = {
+ ADMIN: 'admin',
+ DATA: 'data',
+ OTHER: 'other'
+};
diff --git a/src/ui/ui_exports.js b/src/ui/ui_exports.js
index 38a87d992a827..fefca9397db5c 100644
--- a/src/ui/ui_exports.js
+++ b/src/ui/ui_exports.js
@@ -119,6 +119,7 @@ export default class UiExports {
case 'managementSections':
case 'devTools':
case 'docViews':
+ case 'home':
case 'hacks':
return (plugin, spec) => {
this.aliases[type] = _.union(this.aliases[type] || [], spec);
diff --git a/ui_framework/dist/ui_framework.css b/ui_framework/dist/ui_framework.css
index 20b4c7604601d..b863d9f03b36e 100644
--- a/ui_framework/dist/ui_framework.css
+++ b/ui_framework/dist/ui_framework.css
@@ -3741,6 +3741,7 @@ main {
.kuiTab.kuiTab-isSelected {
cursor: default;
color: #191E23;
+ background-color: #FFF;
border-bottom-color: #FFF; }
.theme-dark .kuiTab.kuiTab-isSelected {
color: #cecece;
@@ -4111,6 +4112,12 @@ main {
.kuiVerticalRhythmSmall + .kuiVerticalRhythmSmall {
margin-top: 5px; }
+.kuiVerticalRhythmLarge + .kuiVerticalRhythmLarge {
+ margin-top: 20px; }
+
+.kuiVerticalRhythmXLarge + .kuiVerticalRhythmXLarge {
+ margin-top: 40px; }
+
.kuiView {
background-color: #FFF;
-webkit-box-flex: 1;
diff --git a/ui_framework/src/components/tabs/_tabs.scss b/ui_framework/src/components/tabs/_tabs.scss
index f6d6c6d8eba3f..5027a4db8c91d 100644
--- a/ui_framework/src/components/tabs/_tabs.scss
+++ b/ui_framework/src/components/tabs/_tabs.scss
@@ -74,6 +74,7 @@
&.kuiTab-isSelected {
cursor: default;
color: $kuiFontColor;
+ background-color: $tabBackgroundColor;
border-bottom-color: $tabBackgroundColor;
@include darkTheme {
diff --git a/ui_framework/src/components/vertical_rhythm/_index.scss b/ui_framework/src/components/vertical_rhythm/_index.scss
index b62be1055988c..1ff1d258b9e9f 100644
--- a/ui_framework/src/components/vertical_rhythm/_index.scss
+++ b/ui_framework/src/components/vertical_rhythm/_index.scss
@@ -1,4 +1,6 @@
$verticalRhythmSpacing: 10px;
$verticalRhythmSmallSpacing: 5px;
+$verticalRhythmLargeSpacing: 20px;
+$verticalRhythmXLargeSpacing: 40px;
@import "vertical_rhythm";
diff --git a/ui_framework/src/components/vertical_rhythm/_vertical_rhythm.scss b/ui_framework/src/components/vertical_rhythm/_vertical_rhythm.scss
index 9f6d2ee6712b7..149007f840204 100644
--- a/ui_framework/src/components/vertical_rhythm/_vertical_rhythm.scss
+++ b/ui_framework/src/components/vertical_rhythm/_vertical_rhythm.scss
@@ -9,3 +9,15 @@
margin-top: $verticalRhythmSmallSpacing;
}
}
+
+.kuiVerticalRhythmLarge {
+ & + & {
+ margin-top: $verticalRhythmLargeSpacing;
+ }
+}
+
+.kuiVerticalRhythmXLarge {
+ & + & {
+ margin-top: $verticalRhythmXLargeSpacing;
+ }
+}