From 3d1ac33858078111e46ed3bb55662c3975de0c64 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 2 Nov 2017 12:58:46 -0600 Subject: [PATCH] Kibana Home page - phase one (#14673) * make kibana home app default when defaultAppId not set * make kibana icon link to home page, add react-router for routing within home app * directory registry * add href to directory listings * add tabs to directory page * add promo section to home page * home page styling * use kuiFlex components to display directory in columns * fix react array missing key warning * add icons * remove feature directory title from home page, change data sources to integrations * add tutorials registry * fix rebase mistake * start tutorial component * wrap tutorial registration in helper function to hide implemenation details * add constants for categry and instruction variant ids * filter tutorial directory by tab category * remove later phase stuff * clean-up feature directory styling * add kbnDirectory to uiExports * remove unused file * cleanup timelion plugin definition * fix lint errors * css work recommended by formgeist review * cleanup from pairing session with snide * rename kbnDirectory registry to featureCatalogue, rename kbnDirectory uiExports to home * update kibana index uses name to match ui-exports name * remove carot from package versions * remove kuiViewContent--constrainedWidth from feature directory view * updates from Stacey-Gammon review * import FeatureCatalogueCategory instead of passing as parameter * wrap KuiButton in href to fix button click bug * remove conditional check for ADMIN and DATA feature category tabs * consider apps for the 'OTHER' tab as anything not in ADMIN or DATA * remove temp variable tabs and just store in this * avoid overwriting kui class styles * prefix class names with home * updates from timroes review --- config/kibana.yml | 2 +- package.json | 3 +- src/core_plugins/kibana/index.js | 3 +- .../kibana/public/assets/app_apm.svg | 30 +++++ .../kibana/public/assets/app_dashboard.svg | 8 ++ .../kibana/public/assets/app_devtools.svg | 7 + .../kibana/public/assets/app_discover.svg | 8 ++ .../kibana/public/assets/app_logging.svg | 11 ++ .../kibana/public/assets/app_monitoring.svg | 7 + .../kibana/public/assets/app_security.svg | 6 + .../kibana/public/assets/app_timelion.svg | 6 + .../kibana/public/assets/app_visualize.svg | 7 + .../kibana/public/dashboard/index.js | 13 ++ .../kibana/public/dev_tools/index.js | 13 ++ .../kibana/public/discover/index.js | 13 ++ .../home/components/feature_directory.js | 124 ++++++++++++++++++ .../kibana/public/home/components/home.js | 121 +++++++++++++++++ .../kibana/public/home/components/home_app.js | 47 +++++++ .../kibana/public/home/components/synopsis.js | 43 ++++++ .../public/home/components/synopsis.less | 12 ++ src/core_plugins/kibana/public/home/home.less | 28 ++++ .../kibana/public/home/home_ng_wrapper.html | 4 + src/core_plugins/kibana/public/home/index.js | 29 ++++ src/core_plugins/kibana/public/kibana.js | 1 + .../management/sections/indices/index.js | 13 ++ .../management/sections/objects/index.js | 13 ++ .../management/sections/settings/index.js | 13 ++ .../kibana/public/visualize/index.js | 12 ++ src/core_plugins/timelion/index.js | 6 +- .../timelion/public/register_feature.js | 13 ++ .../directives/global_nav/global_nav.html | 36 ++--- .../directives/global_nav/global_nav.js | 6 +- src/ui/public/registry/feature_catalogue.js | 14 ++ src/ui/ui_exports.js | 1 + ui_framework/dist/ui_framework.css | 7 + ui_framework/src/components/tabs/_tabs.scss | 1 + .../components/vertical_rhythm/_index.scss | 2 + .../vertical_rhythm/_vertical_rhythm.scss | 12 ++ 38 files changed, 672 insertions(+), 23 deletions(-) create mode 100644 src/core_plugins/kibana/public/assets/app_apm.svg create mode 100644 src/core_plugins/kibana/public/assets/app_dashboard.svg create mode 100644 src/core_plugins/kibana/public/assets/app_devtools.svg create mode 100644 src/core_plugins/kibana/public/assets/app_discover.svg create mode 100644 src/core_plugins/kibana/public/assets/app_logging.svg create mode 100644 src/core_plugins/kibana/public/assets/app_monitoring.svg create mode 100644 src/core_plugins/kibana/public/assets/app_security.svg create mode 100644 src/core_plugins/kibana/public/assets/app_timelion.svg create mode 100644 src/core_plugins/kibana/public/assets/app_visualize.svg create mode 100644 src/core_plugins/kibana/public/home/components/feature_directory.js create mode 100644 src/core_plugins/kibana/public/home/components/home.js create mode 100644 src/core_plugins/kibana/public/home/components/home_app.js create mode 100644 src/core_plugins/kibana/public/home/components/synopsis.js create mode 100644 src/core_plugins/kibana/public/home/components/synopsis.less create mode 100644 src/core_plugins/kibana/public/home/home.less create mode 100644 src/core_plugins/kibana/public/home/home_ng_wrapper.html create mode 100644 src/core_plugins/kibana/public/home/index.js create mode 100644 src/core_plugins/timelion/public/register_feature.js create mode 100644 src/ui/public/registry/feature_catalogue.js 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} + +

+ + {title} + +

+

+ {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; + } +}