From 50dfd3e01a28aabfd1b57aa39180608dd4463930 Mon Sep 17 00:00:00 2001 From: Catherine Liu Date: Thu, 2 Jul 2020 02:42:57 -0700 Subject: [PATCH] Redesigns home page Adds auto scroll to advanced setting provided in the URL hash Registered plugins to feature catalogue to be displayed on home page Rearrange add data section Fix solution panel conditional rendering Removed extraneous import Remmoved environment update from observability plugin Registrered features to feature catalog for ingest manager, ml, and index lifecycle management Fixed import Added max width to Kibana solution card Fixed feature id Fixed enterprise search link Updated solutions logos Fixed beta label on ingest manager card Fixed logos Cleaned up CSS Change home route redirects user to advanced settings Conditionally renders default route advanced setting link Restored app search logo Hides graph description on home page on basic license Style home page header and other misc design tweaks (#72481) --- src/plugins/advanced_settings/kibana.json | 2 +- .../management_app/advanced_settings.tsx | 13 + .../management_app/components/field/field.tsx | 1 + .../advanced_settings/public/plugin.ts | 17 +- src/plugins/advanced_settings/public/types.ts | 3 + src/plugins/home/common/constants.ts | 21 + .../application/components/_add_data.scss | 63 +-- .../public/application/components/_home.scss | 29 ++ .../public/application/components/_index.scss | 1 + .../components/_solutions_panel.scss | 100 ++++ .../application/components/_synopsis.scss | 4 + .../public/application/components/add_data.js | 320 ------------- .../application/components/add_data.test.js | 68 --- .../components/app_navigation_handler.ts | 1 + .../change_home_route.test.tsx | 18 + .../change_home_route/change_home_route.tsx | 65 +++ .../components/change_home_route/index.tsx | 20 + .../public/application/components/home.js | 288 ++++++++---- .../public/application/components/home_app.js | 4 - .../components/solutions_panel/index.ts | 20 + .../solutions_panel/solutions_panel.tsx | 433 ++++++++++++++++++ .../solutions_panel/solutions_title.tsx | 53 +++ ...ound_enterprise_search_bottom_right_2x.png | Bin 0 -> 236 bytes ...ckground_enterprise_search_top_left_2x.png | Bin 0 -> 951 bytes .../background_kibana_bottom_right_2x.png | Bin 0 -> 2353 bytes .../assets/background_kibana_top_left_2x.png | Bin 0 -> 2125 bytes .../background_observability_top_right_2x.png | Bin 0 -> 312 bytes ...ckground_security_solution_top_left_2x.png | Bin 0 -> 764 bytes src/plugins/home/public/index.ts | 1 + src/plugins/home/public/plugin.ts | 30 +- .../feature_catalogue_registry.ts | 18 +- src/plugins/management/public/plugin.ts | 2 +- x-pack/plugins/apm/public/plugin.ts | 1 + x-pack/plugins/graph/public/plugin.ts | 14 +- .../public/plugin.tsx | 20 +- .../public/types.ts | 2 + x-pack/plugins/infra/server/features.ts | 12 +- x-pack/plugins/ingest_manager/kibana.json | 4 +- .../plugins/ingest_manager/public/plugin.ts | 20 +- .../plugins/ingest_manager/server/plugin.ts | 3 + .../plugins/ml/common/types/capabilities.ts | 3 +- x-pack/plugins/ml/public/register_feature.ts | 19 + x-pack/plugins/ml/server/plugin.ts | 2 +- x-pack/plugins/observability/kibana.json | 3 + x-pack/plugins/observability/public/plugin.ts | 28 +- .../security_solution/common/constants.ts | 2 +- .../security_solution/public/plugin.tsx | 28 +- .../plugins/snapshot_restore/public/plugin.ts | 24 +- 48 files changed, 1181 insertions(+), 599 deletions(-) create mode 100644 src/plugins/home/common/constants.ts create mode 100644 src/plugins/home/public/application/components/_solutions_panel.scss delete mode 100644 src/plugins/home/public/application/components/add_data.js delete mode 100644 src/plugins/home/public/application/components/add_data.test.js create mode 100644 src/plugins/home/public/application/components/change_home_route/change_home_route.test.tsx create mode 100644 src/plugins/home/public/application/components/change_home_route/change_home_route.tsx create mode 100644 src/plugins/home/public/application/components/change_home_route/index.tsx create mode 100644 src/plugins/home/public/application/components/solutions_panel/index.ts create mode 100644 src/plugins/home/public/application/components/solutions_panel/solutions_panel.tsx create mode 100644 src/plugins/home/public/application/components/solutions_panel/solutions_title.tsx create mode 100644 src/plugins/home/public/assets/background_enterprise_search_bottom_right_2x.png create mode 100644 src/plugins/home/public/assets/background_enterprise_search_top_left_2x.png create mode 100644 src/plugins/home/public/assets/background_kibana_bottom_right_2x.png create mode 100644 src/plugins/home/public/assets/background_kibana_top_left_2x.png create mode 100644 src/plugins/home/public/assets/background_observability_top_right_2x.png create mode 100644 src/plugins/home/public/assets/background_security_solution_top_left_2x.png diff --git a/src/plugins/advanced_settings/kibana.json b/src/plugins/advanced_settings/kibana.json index 8cf9b9c656d8f..2f83ccf332dbd 100644 --- a/src/plugins/advanced_settings/kibana.json +++ b/src/plugins/advanced_settings/kibana.json @@ -3,6 +3,6 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": ["management"], + "requiredPlugins": ["management", "home"], "requiredBundles": ["kibanaReact"] } diff --git a/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx b/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx index d8853015d362a..dd4383d76a738 100644 --- a/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx +++ b/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx @@ -114,6 +114,19 @@ export class AdvancedSettingsComponent extends Component< filteredSettings: this.mapSettings(Query.execute(query, this.settings)), }); }); + + // scrolls to setting provided in the URL hash + const { hash } = window.location; + if (hash !== '') { + setTimeout(() => { + const id = hash.replace('#', ''); + const element = document.getElementById(id); + if (element) { + element.scrollIntoView(); + window.scrollBy(0, -48); // offsets scroll by height of the global nav + } + }, 0); + } } componentWillUnmount() { diff --git a/src/plugins/advanced_settings/public/management_app/components/field/field.tsx b/src/plugins/advanced_settings/public/management_app/components/field/field.tsx index 32bfc0826e7c4..83b702ecef0e9 100644 --- a/src/plugins/advanced_settings/public/management_app/components/field/field.tsx +++ b/src/plugins/advanced_settings/public/management_app/components/field/field.tsx @@ -671,6 +671,7 @@ export class Field extends PureComponent { return ( { - public setup(core: CoreSetup, { management }: AdvancedSettingsPluginSetup) { + public setup(core: CoreSetup, { management, home }: AdvancedSettingsPluginSetup) { const kibanaSection = management.sections.section.kibana; kibanaSection.registerApp({ @@ -44,6 +45,20 @@ export class AdvancedSettingsPlugin }, }); + if (home) { + home.featureCatalogue.register({ + id: 'advanced_settings', + title, + description: i18n.translate('xpack.advancedSettings.featureCatalogueTitle', { + defaultMessage: 'Customize your Kibana experience', + }), + icon: 'gear', // TODO: Do we want to use this icon here? + path: '/app/management/kibana/settings', + showOnHomePage: true, + category: FeatureCatalogueCategory.ADMIN, + }); + } + return { component: component.setup, }; diff --git a/src/plugins/advanced_settings/public/types.ts b/src/plugins/advanced_settings/public/types.ts index a233b3debab8d..cc59f52b1f30f 100644 --- a/src/plugins/advanced_settings/public/types.ts +++ b/src/plugins/advanced_settings/public/types.ts @@ -18,6 +18,8 @@ */ import { ComponentRegistry } from './component_registry'; +import { HomePublicPluginSetup } from '../../home/public'; + import { ManagementSetup } from '../../management/public'; export interface AdvancedSettingsSetup { @@ -29,6 +31,7 @@ export interface AdvancedSettingsStart { export interface AdvancedSettingsPluginSetup { management: ManagementSetup; + home?: HomePublicPluginSetup; } export { ComponentRegistry }; diff --git a/src/plugins/home/common/constants.ts b/src/plugins/home/common/constants.ts new file mode 100644 index 0000000000000..a9457a9d3307c --- /dev/null +++ b/src/plugins/home/common/constants.ts @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const PLUGIN_ID = 'home'; +export const HOME_APP_BASE_PATH = `/app/${PLUGIN_ID}`; diff --git a/src/plugins/home/public/application/components/_add_data.scss b/src/plugins/home/public/application/components/_add_data.scss index 836b34227a37c..115f957059475 100644 --- a/src/plugins/home/public/application/components/_add_data.scss +++ b/src/plugins/home/public/application/components/_add_data.scss @@ -1,63 +1,8 @@ -.homAddData__card { - border: none; - box-shadow: none; -} - -.homAddData__cardDivider { - position: relative; - - &:after { - position: absolute; - content: ''; - width: 1px; - right: -$euiSizeS; - top: 0; - bottom: 0; - background: $euiBorderColor; - } -} - -.homAddData__icon { - width: $euiSizeXL * 2; - height: $euiSizeXL * 2; -} - -.homAddData__footerItem--highlight { - background-color: tintOrShade($euiColorPrimary, 90%, 70%); - padding: $euiSize; -} - -.homAddData__footerItem { +.homAddData__image { text-align: center; -} - -.homAddData__logo { - margin-left: $euiSize; -} - -@include euiBreakpoint('xs', 's') { - .homeAddData__flexGroup { - flex-wrap: wrap; - } -} - -@include euiBreakpoint('xs', 's', 'm') { - .homAddDat__flexTablet { - flex-direction: column; - } - - .homAddData__cardDivider:after { - display: none; - } - - .homAddData__cardDivider { - flex-grow: 0 !important; - flex-basis: 100% !important; - } -} -@include euiBreakpoint('l', 'xl') { - .homeAddData__flexGroup { - flex-wrap: nowrap; + .euiImage__img { + max-height: $euiSize * 16; // too tall if only two "Add data" features are enabled + width: auto; } } diff --git a/src/plugins/home/public/application/components/_home.scss b/src/plugins/home/public/application/components/_home.scss index 4101f6519829b..77d401d5af43e 100644 --- a/src/plugins/home/public/application/components/_home.scss +++ b/src/plugins/home/public/application/components/_home.scss @@ -1,3 +1,32 @@ +// Local page vars +$homePageHeaderHeight: $euiSize * 8; +$homePageWidth: 1200px; + +.homPageHeader { + height: $homePageHeaderHeight; + margin: 0 auto; + max-width: $homePageWidth; + padding: $euiSizeXL $euiSize 0; +} + +.homPageContainer { + min-height: calc(100vh - #{$homePageHeaderHeight}); + background-color: $euiColorEmptyShade; + border-top: 1px solid $euiColorLightShade; +} + +.homPage { + display: flex; + max-width: $homePageWidth; + margin: 0 auto; + padding: 0 $euiSize $euiSizeXL; + background-color: transparent; +} + +.homHome__synopsisItem { + max-width: 50%; +} + @include euiBreakpoint('xs', 's', 'm') { .homHome__synopsisItem { flex-basis: 100% !important; diff --git a/src/plugins/home/public/application/components/_index.scss b/src/plugins/home/public/application/components/_index.scss index 870099ffb350e..59bcdd0e8f289 100644 --- a/src/plugins/home/public/application/components/_index.scss +++ b/src/plugins/home/public/application/components/_index.scss @@ -8,6 +8,7 @@ @import 'add_data'; @import 'home'; @import 'sample_data_set_cards'; +@import 'solutions_panel'; @import 'synopsis'; @import 'welcome'; diff --git a/src/plugins/home/public/application/components/_solutions_panel.scss b/src/plugins/home/public/application/components/_solutions_panel.scss new file mode 100644 index 0000000000000..1b2d246818574 --- /dev/null +++ b/src/plugins/home/public/application/components/_solutions_panel.scss @@ -0,0 +1,100 @@ +.homSolutionsPanel { + margin-top: -$euiSizeXL; + min-height: $euiSize*16; + display: flex; + + .homSolutionsPanel--restrictHalfWidth { + max-width: 50%; + } + + .homSolutionsPanel__solutionPanel { + display: flex; + align-items: stretch; + + .homSolutionsPanel__solutionTitle { + padding: $euiSize; + + .euiToken { + padding: $euiSizeS; + } + } + + .homSolutionsPanel__CTA { + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + padding: $euiSize; + } + } + + .homSolutionsPanel__header { + border-radius: $euiBorderRadius 0 0 $euiBorderRadius; + color: $euiColorEmptyShade; + + img { + position: absolute; + width: auto; + } + } + + .homSolutionsPanel__enterpriseSearch { + .homSolutionsPanel__enterpriseSearchHeader { + background-color: #017d73; + + .homSolutionsPanel__enterpriseSearchTopLeftImage img { + top: $euiSizeS; + left: 0; + height: $euiSizeXL; + } + + .homSolutionsPanel__enterpriseSearchBottomRightImage img { + right: $euiSizeS; + bottom: $euiSizeS; + height: $euiSizeXL; + } + } + } + + .homSolutionsPanel__observability { + .homSolutionsPanel__observabilityHeader { + background-color: #c42373; + + .homSolutionsPanel__observabilityTopRightImage img { + top: $euiSizeS; + right: $euiSizeS; + height: $euiSizeXL; + } + } + } + + .homSolutionsPanel__securitySolution { + .homSolutionsPanel__securitySolutionHeader { + background-color: #343741; + + .homSolutionsPanel__securitySolutionTopLeftImage img { + top: $euiSizeS; + left: $euiSizeS; + height: $euiSizeXXL; + } + } + } + + .homSolutionsPanel__kibana { + .homSolutionsPanel__kibanaHeader { + background-color: #006bb4; + + .homSolutionsPanel__kibanaTopLeftImage img { + top: 0; + left: 0; + height: $euiSizeXXL * 4; + } + + .homSolutionsPanel__kibanaBottomRightImage img { + right: 0; + bottom: 0; + height: $euiSizeXXL * 4; + } + } + } +} diff --git a/src/plugins/home/public/application/components/_synopsis.scss b/src/plugins/home/public/application/components/_synopsis.scss index 49e71f159fe6f..3eac2bc9705e0 100644 --- a/src/plugins/home/public/application/components/_synopsis.scss +++ b/src/plugins/home/public/application/components/_synopsis.scss @@ -5,6 +5,10 @@ box-shadow: none; } + .homSynopsis__cardTitle { + display: flex; + } + // SASSTODO: Fix in EUI .euiCard__content { padding-top: 0 !important; diff --git a/src/plugins/home/public/application/components/add_data.js b/src/plugins/home/public/application/components/add_data.js deleted file mode 100644 index c35b7b04932fb..0000000000000 --- a/src/plugins/home/public/application/components/add_data.js +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; -import { getServices } from '../kibana_services'; - -import { - EuiButton, - EuiLink, - EuiPanel, - EuiTitle, - EuiSpacer, - EuiFlexGroup, - EuiFlexItem, - EuiText, - EuiCard, - EuiIcon, - EuiHorizontalRule, - EuiFlexGrid, -} from '@elastic/eui'; - -const AddDataUi = ({ apmUiEnabled, isNewKibanaInstance, intl, mlEnabled }) => { - const basePath = getServices().getBasePath(); - - const renderCards = () => { - const apmData = { - title: intl.formatMessage({ - id: 'home.addData.apm.nameTitle', - defaultMessage: 'APM', - }), - description: intl.formatMessage({ - id: 'home.addData.apm.nameDescription', - defaultMessage: - 'APM automatically collects in-depth performance metrics and errors from inside your applications.', - }), - ariaDescribedby: 'aria-describedby.addAmpButtonLabel', - }; - const loggingData = { - title: intl.formatMessage({ - id: 'home.addData.logging.nameTitle', - defaultMessage: 'Logs', - }), - description: intl.formatMessage({ - id: 'home.addData.logging.nameDescription', - defaultMessage: - 'Ingest logs from popular data sources and easily visualize in preconfigured dashboards.', - }), - ariaDescribedby: 'aria-describedby.addLogDataButtonLabel', - }; - const metricsData = { - title: intl.formatMessage({ - id: 'home.addData.metrics.nameTitle', - defaultMessage: 'Metrics', - }), - description: intl.formatMessage({ - id: 'home.addData.metrics.nameDescription', - defaultMessage: - 'Collect metrics from the operating system and services running on your servers.', - }), - ariaDescribedby: 'aria-describedby.addMetricsButtonLabel', - }; - const siemData = { - title: intl.formatMessage({ - id: 'home.addData.securitySolution.nameTitle', - defaultMessage: 'SIEM + Endpoint Security', - }), - description: intl.formatMessage({ - id: 'home.addData.securitySolution.nameDescription', - defaultMessage: - 'Protect hosts, analyze security information and events, hunt threats, automate detections, and create cases.', - }), - ariaDescribedby: 'aria-describedby.addSiemButtonLabel', - }; - - const getApmCard = () => ( - - {apmData.description}} - footer={ - - - - } - /> - - ); - - return ( - - - - - - - - - -

- -

-
-
-
- - - {apmUiEnabled !== false && getApmCard()} - - - {loggingData.description} - } - footer={ - - - - } - /> - - - - {metricsData.description} - } - footer={ - - - - } - /> - - -
- - - - - - - - -

- -

-
-
-
- - {siemData.description}} - footer={ - - - - } - /> -
-
- ); - }; - - const footerItemClasses = classNames('homAddData__footerItem', { - 'homAddData__footerItem--highlight': isNewKibanaInstance, - }); - - return ( - - {renderCards()} - - - - - - - - - - - - - - - {mlEnabled !== false ? ( - - - - - - - - - - - ) : null} - - - - - - - - - - - - - ); -}; - -AddDataUi.propTypes = { - apmUiEnabled: PropTypes.bool.isRequired, - mlEnabled: PropTypes.bool.isRequired, - isNewKibanaInstance: PropTypes.bool.isRequired, -}; - -export const AddData = injectI18n(AddDataUi); diff --git a/src/plugins/home/public/application/components/add_data.test.js b/src/plugins/home/public/application/components/add_data.test.js deleted file mode 100644 index 9457f766409b8..0000000000000 --- a/src/plugins/home/public/application/components/add_data.test.js +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { AddData } from './add_data'; -import { shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { getServices } from '../kibana_services'; - -jest.mock('../kibana_services', () => { - const mock = { - getBasePath: jest.fn(() => 'path'), - }; - return { - getServices: () => mock, - }; -}); - -beforeEach(() => { - jest.clearAllMocks(); -}); - -test('render', () => { - const component = shallowWithIntl( - - ); - expect(component).toMatchSnapshot(); // eslint-disable-line - expect(getServices().getBasePath).toHaveBeenCalledTimes(1); -}); - -test('mlEnabled', () => { - const component = shallowWithIntl( - - ); - expect(component).toMatchSnapshot(); // eslint-disable-line - expect(getServices().getBasePath).toHaveBeenCalledTimes(1); -}); - -test('apmUiEnabled', () => { - const component = shallowWithIntl( - - ); - expect(component).toMatchSnapshot(); // eslint-disable-line - expect(getServices().getBasePath).toHaveBeenCalledTimes(1); -}); - -test('isNewKibanaInstance', () => { - const component = shallowWithIntl( - - ); - expect(component).toMatchSnapshot(); // eslint-disable-line - expect(getServices().getBasePath).toHaveBeenCalledTimes(1); -}); diff --git a/src/plugins/home/public/application/components/app_navigation_handler.ts b/src/plugins/home/public/application/components/app_navigation_handler.ts index 6e78af7f42f52..61d85c033b544 100644 --- a/src/plugins/home/public/application/components/app_navigation_handler.ts +++ b/src/plugins/home/public/application/components/app_navigation_handler.ts @@ -17,6 +17,7 @@ * under the License. */ +import { MouseEvent } from 'react'; import { getServices } from '../kibana_services'; export const createAppNavigationHandler = (targetUrl: string) => (event: MouseEvent) => { diff --git a/src/plugins/home/public/application/components/change_home_route/change_home_route.test.tsx b/src/plugins/home/public/application/components/change_home_route/change_home_route.test.tsx new file mode 100644 index 0000000000000..9880b336e76e5 --- /dev/null +++ b/src/plugins/home/public/application/components/change_home_route/change_home_route.test.tsx @@ -0,0 +1,18 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ diff --git a/src/plugins/home/public/application/components/change_home_route/change_home_route.tsx b/src/plugins/home/public/application/components/change_home_route/change_home_route.tsx new file mode 100644 index 0000000000000..1dc1bcc60ae4c --- /dev/null +++ b/src/plugins/home/public/application/components/change_home_route/change_home_route.tsx @@ -0,0 +1,65 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { FunctionComponent } from 'react'; +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { HOME_APP_BASE_PATH } from '../../../../common/constants'; +import { getServices } from '../../kibana_services'; +import { createAppNavigationHandler } from '../app_navigation_handler'; + +interface Props { + defaultRoute?: string; +} + +export const ChangeHomeRoute: FunctionComponent = ({ defaultRoute }) => { + const { uiSettings } = getServices(); + const changeDefaultRoute = () => uiSettings.set('defaultRoute', defaultRoute); + + return ( + + + +

+ +

+
+
+ + + + + +
+ ); +}; + +ChangeHomeRoute.defaultProps = { + defaultRoute: HOME_APP_BASE_PATH, +}; diff --git a/src/plugins/home/public/application/components/change_home_route/index.tsx b/src/plugins/home/public/application/components/change_home_route/index.tsx new file mode 100644 index 0000000000000..430100a29b161 --- /dev/null +++ b/src/plugins/home/public/application/components/change_home_route/index.tsx @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from './change_home_route'; diff --git a/src/plugins/home/public/application/components/home.js b/src/plugins/home/public/application/components/home.js index f8769bfd0d618..f44fb914c7fda 100644 --- a/src/plugins/home/public/application/components/home.js +++ b/src/plugins/home/public/application/components/home.js @@ -17,30 +17,27 @@ * under the License. */ -import React, { Component } from 'react'; +import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { Synopsis } from './synopsis'; -import { AddData } from './add_data'; +import { ChangeHomeRoute } from './change_home_route'; +import { SolutionsPanel } from './solutions_panel'; import { FormattedMessage } from '@kbn/i18n/react'; import { - EuiButton, - EuiPage, - EuiPanel, + EuiButtonEmpty, EuiTitle, - EuiSpacer, EuiFlexGroup, EuiFlexItem, - EuiFlexGrid, - EuiText, - EuiPageBody, EuiScreenReaderOnly, + EuiSpacer, + EuiHorizontalRule, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { Welcome } from './welcome'; import { getServices } from '../kibana_services'; -import { FeatureCatalogueCategory } from '../../services'; +import { HOME_APP_BASE_PATH } from '../../../common/constants'; import { createAppNavigationHandler } from './app_navigation_handler'; const KEY_ENABLE_WELCOME = 'home:welcome:show'; @@ -116,105 +113,204 @@ export class Home extends Component { this._isMounted && this.setState({ isWelcomeEnabled: false }); }; - renderDirectories = (category) => { - const { addBasePath, directories } = this.props; - return directories - .filter((directory) => { - return directory.showOnHomePage && directory.category === category; - }) - .map((directory) => { - return ( - - - - ); - }); + findDirectoryById = (id) => + this.props.directories.find((directory) => directory.showOnHomePage && directory.id === id); + + renderDirectory = (directory) => { + const { addBasePath } = this.props; + + return directory ? ( + + + + ) : null; }; renderNormal() { - const { apmUiEnabled, mlEnabled } = this.props; + const { addBasePath, directories } = this.props; + + const fileDataVisualizer = this.findDirectoryById('ml_file_data_visualizer'); + const ingestManager = this.findDirectoryById('ingestManager'); + const security = this.findDirectoryById('security'); + const monitoring = this.findDirectoryById('monitoring'); + const snapshotRestore = this.findDirectoryById('snapshot_restore'); + const indexLifecycleManagement = this.findDirectoryById('index_lifecycle_management'); + const devTools = this.findDirectoryById('console'); + const stackManagement = this.findDirectoryById('stack-management'); + const advancedSettings = this.findDirectoryById('advanced_settings'); + + console.log({ directories }); return ( - - - -

- -

-
- - - - - - + +
+ - - -

+ +

+ +

+
+ + +

-

+

- - - {this.renderDirectories(FeatureCatalogueCategory.DATA)} - -
+
- - -

- -

-
- - - {this.renderDirectories(FeatureCatalogueCategory.ADMIN)} - -
+ + + + {i18n.translate('home.pageHeader.addDataButtonLabel', { + defaultMessage: 'Add data', + })} + + + {stackManagement ? ( + + + {i18n.translate('home.pageHeader.stackManagementButtonLabel', { + defaultMessage: 'Manage stack', + })} + + + ) : null} + {devTools ? ( + + + {i18n.translate('home.pageHeader.devToolsButtonLabel', { + defaultMessage: 'Dev tools', + })} + + + ) : null} + {/* + + {i18n.translate('home.pageHeader.appDirectoryButtonLabel', { + defaultMessage: 'App directory', + })} + + */} +
+
+
+
+
+ +
- - - - - -

- -

-
- - - - -
-
- - + + + +
+ + + +

+ {i18n.translate('home.addData.sectionTitle', { + defaultMessage: 'Add your data', + })} +

+
+
+ + + + + +
+ + + + + + + + + + {this.renderDirectory(ingestManager)} + {this.renderDirectory(fileDataVisualizer)} + + + +
+ + {security || monitoring || snapshotRestore || indexLifecycleManagement ? ( + + + + +
+ +

+ {i18n.translate('home.manageData.sectionTitle', { + defaultMessage: 'Manage your data', + })} +

+
+ + + + + {this.renderDirectory(security)} + {this.renderDirectory(monitoring)} + {this.renderDirectory(snapshotRestore)} + {this.renderDirectory(indexLifecycleManagement)} + +
+
+ ) : null} + + {advancedSettings && ( + + + + + )} +
+
+
); } @@ -262,11 +358,9 @@ Home.propTypes = { category: PropTypes.string.isRequired, }) ), - apmUiEnabled: PropTypes.bool.isRequired, find: PropTypes.func.isRequired, localStorage: PropTypes.object.isRequired, urlBasePath: PropTypes.string.isRequired, - mlEnabled: PropTypes.bool.isRequired, telemetry: PropTypes.shape({ telemetryService: PropTypes.any, telemetryNotifications: PropTypes.any, diff --git a/src/plugins/home/public/application/components/home_app.js b/src/plugins/home/public/application/components/home_app.js index 648915b6dae0c..53f094943480d 100644 --- a/src/plugins/home/public/application/components/home_app.js +++ b/src/plugins/home/public/application/components/home_app.js @@ -48,8 +48,6 @@ export function HomeApp({ directories }) { } = getServices(); const environment = environmentService.getEnvironment(); const isCloudEnabled = environment.cloud; - const mlEnabled = environment.ml; - const apmUiEnabled = environment.apmUi; const renderTutorialDirectory = (props) => { return ( @@ -87,8 +85,6 @@ export function HomeApp({ directories }) { string; + findDirectoryById: (id: string) => FeatureCatalogueEntry; +} + +// TODO: Bolding the first word/verb won't look write in other languages +const getActionText = ({ verb, text }: { verb: string; text: string }): JSX.Element => ( + +

+ {verb} {text} +

+
+); + +// TODO: Should this live here? Should it be registered per app? +const solutionCTAs: { [key: string]: any } = { + enterpriseSearch: { + websiteSearch: { + verb: i18n.translate('home.solutionsPanel.enterpriseSearch.firstActionVerb', { + defaultMessage: 'Build', + description: + 'The first word of this sentence is bolded. Full sentence: "Build a powerful website search."', + }), + text: i18n.translate('home.solutionsPanel.enterpriseSearch.firstActionText', { + defaultMessage: 'a powerful website search.', + description: 'Full sentence: "Build a powerful website search."', + }), + }, + appSearch: { + verb: i18n.translate('home.solutionsPanel.enterpriseSearch.secondActionVerb', { + defaultMessage: 'Search', + description: + 'The first word of this sentence is bolded. Full sentence: "Search any data from any application."', + }), + text: i18n.translate('home.solutionsPanel.enterpriseSearch.secondActionText', { + defaultMessage: 'any data from any application.', + description: 'Full sentence: "Search any data from any application."', + }), + }, + workplaceSearch: { + verb: i18n.translate('home.solutionsPanel.enterpriseSearch.thirdActionVerb', { + defaultMessage: 'Unify', + description: + 'The first word of this sentence is bolded. Full sentence: "Unify searchable workplace content."', + }), + text: i18n.translate('home.solutionsPanel.enterpriseSearch.thirdActionText', { + defaultMessage: 'searchable workplace content.', + description: 'Full sentence: "Unify searchable workplace content."', + }), + }, + }, + observability: { + metrics: { + verb: i18n.translate('home.solutionsPanel.observability.firstActionVerb', { + defaultMessage: 'Monitor', + description: + 'The first word of this sentence is bolded. Full sentence: "Monitor all infrastructure metrics."', + }), + text: i18n.translate('home.solutionsPanel.observability.firstActionText', { + defaultMessage: 'all infrastructure metrics.', + description: 'Full sentence: "Monitor all infrastructure metrics."', + }), + }, + apm: { + verb: i18n.translate('home.solutionsPanel.observability.secondActionVerb', { + defaultMessage: 'Track', + description: + 'The first word of this sentence is bolded. Full sentence: "Track application performance."', + }), + text: i18n.translate('home.solutionsPanel.observability.secondActionText', { + defaultMessage: 'application performance.', + description: 'Full sentence: "Track application performance."', + }), + }, + uptime: { + verb: i18n.translate('home.solutionsPanel.observability.thirdActionVerb', { + defaultMessage: 'Measure', + description: + 'The first word of the following sentence is bolded. Full sentence: "Measure SLAs and react to issues."', + }), + text: i18n.translate('home.solutionsPanel.observability.thirdActionText', { + defaultMessage: 'SLAs and react to issues.', + description: 'Full sentence: "Measure SLAs and react to issues."', + }), + }, + }, + securitySolution: [ + { + verb: i18n.translate('home.solutionsPanel.securitySolution.firstActionVerb', { + defaultMessage: 'Detect', + description: + 'The first word of this sentence is bolded. Full sentence: "Detect critical security events."', + }), + text: i18n.translate('home.solutionsPanel.securitySolution.firstActionText', { + defaultMessage: 'critical security events.', + description: 'Full sentence: "Detect critical security events."', + }), + }, + { + verb: i18n.translate('home.solutionsPanel.securitySolution.secondActionVerb', { + defaultMessage: 'Investigate', + description: + 'The first word of this sentence is bolded. Full sentence: "Investigate incidents and collaborate."', + }), + text: i18n.translate('home.solutionsPanel.securitySolution.secondActionText', { + defaultMessage: 'incidents and collaborate.', + description: 'Full sentence: "Investigate incidents and collaborate."', + }), + }, + { + verb: i18n.translate('home.solutionsPanel.securitySolution.thirdActionVerb', { + defaultMessage: 'Prevent', + description: + 'The first word of the following sentence is bolded. Full sentence: "Prevent threats autonomously."', + }), + text: i18n.translate('home.solutionsPanel.securitySolution.thirdActionText', { + defaultMessage: 'threats autonomously.', + description: 'Full sentence: "Prevent threats autonomously."', + }), + }, + ], + kibana: { + dashboard: { + verb: i18n.translate('home.solutionsPanel.kibana.dashboardVerb', { + defaultMessage: 'Visualize', + description: + 'The first word of this sentence is bolded. Full sentence: "Visualize every aspect of your data."', + }), + text: i18n.translate('home.solutionsPanel.kibana.dashboardText', { + defaultMessage: 'every aspect of your data.', + description: 'Full sentence: "Visualize every aspect of your data."', + }), + }, + discover: { + verb: i18n.translate('home.solutionsPanel.kibana.discoverVerb', { + defaultMessage: 'Search', + description: + 'The first word of this sentence is bolded. Full sentence: "Search and explore your data."', + }), + text: i18n.translate('home.solutionsPanel.kibana.discoverText', { + defaultMessage: 'and explore your data.', + description: 'Full sentence: "Search and explore your data."', + }), + }, + canvas: { + verb: i18n.translate('home.solutionsPanel.kibana.fourthActionVerb', { + defaultMessage: 'Craft', + description: + 'The first word of this sentence is bolded. Full sentence: "Craft pixel-perfect reports."', + }), + text: i18n.translate('home.solutionsPanel.kibana.fourthActionText', { + defaultMessage: 'pixel-perfect reports.', + description: 'Full sentence: "Craft pixel-perfect reports."', + }), + }, + maps: { + verb: i18n.translate('home.solutionsPanel.kibana.thirdActionVerb', { + defaultMessage: 'Plot', + description: + 'The first word of the following sentence is bolded. Full sentence: "Plot your geographic information."', + }), + text: i18n.translate('home.solutionsPanel.kibana.thirdActionText', { + defaultMessage: 'your geographic information.', + description: 'Full sentence: "Plot your geographic information."', + }), + }, + ml: { + verb: i18n.translate('home.solutionsPanel.kibana.fifthActionVerb', { + defaultMessage: 'Detect', + description: + 'The first word of this sentence is bolded. Full sentence: "Detect anomalous events."', + }), + text: i18n.translate('home.solutionsPanel.kibana.fifthActionText', { + defaultMessage: 'anomalous events.', + description: 'Full sentence: "Detect anomalous events."', + }), + }, + graph: { + verb: i18n.translate('home.solutionsPanel.kibana.sixthActionVerb', { + defaultMessage: 'Reveal', + description: + 'The first word of the following sentence is bolded. Full sentence: "Reveal patterns and relationships."', + }), + text: i18n.translate('home.solutionsPanel.kibana.sixthActionText', { + defaultMessage: 'patterns and relationships.', + description: 'Full sentence: "Reveal patterns and relationships."', + }), + }, + }, +}; + +const halfWidthClass = 'homeSolutionsPanel--restrictHalfWidth'; + +export const SolutionsPanel: FunctionComponent = ({ + addBasePath, + findDirectoryById +}) => { + const observability = findDirectoryById('observability'); + const enterpriseSearch = findDirectoryById('appSearch'); + const securitySolution = findDirectoryById('securitySolution'); + + + const addSpacersBetweenReducer = ( + acc: JSX.Element[], + element: JSX.Element, + index: number, + elements: JSX.Element[] + ) => { + acc.push(element); + if (index < elements.length - 1) { + acc.push(); + } + return acc; + }; + + const getActionsBySolution = (solution: string, appIds: string[]): JSX.Element[] => + appIds.reduce((acc: JSX.Element[], appId: string, index: number) => { + const directory = findDirectoryById(appId); + if (directory) { + const CTA = solutionCTAs[solution][appId] as { verb: string; text: string }; + if (CTA) { + // acc.push(directory.description); + acc.push(getActionText(CTA)); + } + } + return acc; + }, [] as JSX.Element[]); + + const enterpriseSearchAppIds = ['webSearch', 'appSearch', 'workplaceSearch']; + const observabilityAppIds = ['metrics', 'apm', 'uptime']; + const kibanaAppIds = ['dashboard', 'discover', 'canvas', 'maps', 'ml', 'graph']; + + const enterpriseSearchActions = getActionsBySolution( + 'enterpriseSearch', + enterpriseSearchAppIds + ).reduce(addSpacersBetweenReducer, []); + const observabilityActions = getActionsBySolution('observability', observabilityAppIds).reduce( + addSpacersBetweenReducer, + [] + ); + const securitySolutionActions = solutionCTAs.securitySolution + .map(getActionText) + .reduce(addSpacersBetweenReducer, []); + const kibanaActions = getActionsBySolution('kibana', kibanaAppIds).reduce( + addSpacersBetweenReducer, + [] + ); + +const halfWidthClass = 'homSolutionsPanel--restrictHalfWidth'; + + return ( + + {enterpriseSearchActions.length || observabilityActions.length || securitySolution ? ( + + + {/* TODO: once app search is merged, register add to feature catalogue and remove hard coded text here */} + + {enterpriseSearchActions.length ? ( + + + + + + + + + + + {enterpriseSearchActions} + + + + + ) : null} + {observabilityActions.length ? ( + + + + + + + + + + {observabilityActions} + + + + + ) : null} + {securitySolution ? ( + + + + + + + + + {securitySolutionActions} + + + + + ) : null} + + + ) : null} +{kibanaActions.length ? ( + + + + + + + + + + {kibanaActions} + + + + ):null} + +);} diff --git a/src/plugins/home/public/application/components/solutions_panel/solutions_title.tsx b/src/plugins/home/public/application/components/solutions_panel/solutions_title.tsx new file mode 100644 index 0000000000000..3503edf885044 --- /dev/null +++ b/src/plugins/home/public/application/components/solutions_panel/solutions_title.tsx @@ -0,0 +1,53 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { FunctionComponent } from 'react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiToken, + EuiTitle, + EuiText, + EuiIcon, + IconType, +} from '@elastic/eui'; + +interface Props { + title: string; + subtitle: string; + iconType: IconType; +} + +export const SolutionsTitle: FunctionComponent = ({ title, subtitle, iconType }) => ( + + +

+ +

+ +

{title}

+
+ +

+ {subtitle} +

+
+
+
+); diff --git a/src/plugins/home/public/assets/background_enterprise_search_bottom_right_2x.png b/src/plugins/home/public/assets/background_enterprise_search_bottom_right_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7f2037d3afb699a50ff074304090ffa8e94dfcc1 GIT binary patch literal 236 zcmeAS@N?(olHy`uVBq!ia0vp^4nSy1^tdQ* z-6YTAy|X6O@O5f`zaoDTtHU~m6QT?|nH0hqo^Un9Fiy~B01EoaCv42$u_s6!E`p>i qs%6I2*>_$dR3piO1uwJzVAR{eH2LttUEhF?VeoYIb6Mw<&;$S-n?_~; literal 0 HcmV?d00001 diff --git a/src/plugins/home/public/assets/background_enterprise_search_top_left_2x.png b/src/plugins/home/public/assets/background_enterprise_search_top_left_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3e6e016bd73a7cf1b49ad4faf7477c21757dd9cf GIT binary patch literal 951 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 SpKGGoz{}mwt=2{Gw|@hbY)Z8N literal 0 HcmV?d00001 diff --git a/src/plugins/home/public/assets/background_observability_top_right_2x.png b/src/plugins/home/public/assets/background_observability_top_right_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..860491733ab65fae3321d6af347ed6b4a8227501 GIT binary patch literal 312 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!oCO|{#S9E$svykh8Km+7D9BhG z1ThT)cdUbxqFZ|6Y*? z!&nrLvHl6|I#68@^Z((g-De!P(^b literal 0 HcmV?d00001 diff --git a/src/plugins/home/public/assets/background_security_solution_top_left_2x.png b/src/plugins/home/public/assets/background_security_solution_top_left_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5cb0358d02f025d092006eb84990300669e06339 GIT binary patch literal 764 zcmV4j_O|qHHBsniEYAXmnwAkHK6cPCnW?q<%y%58^@c)q* z#)XT|fw2>2Qn>m0AjB{!Jh+V*28AcL1LH$jwZe=0zJ(ao3M=;!qf#N_eqg){Q*8}m z`5s~@7XyKSKn%rVArLVXOMyVdP%0Gy5ksLI2()0l2`f@A1R_Skwm_gyAx4q5LZAhM zZKwnU0>(jDcBLQ?PMs4Yvyu>q7@3uZK)~Sj@5D495XT6KX+a=jB%}#}h!L4K1R_R6 zH6Rc%R;mSoIAD8GO$fxz)RSsMAYweo0s?Wsc9RtZ;%4e1O9&3wF0zDJem<{%i#>!0 zG3Go#py8JxULerW7@i=|uo&JT(2y7&A<%G}DX$P{C=AaKXqW+;cL+4ZfUN}x)ct_1 z6$sSzW~wC!)GbD95U9%mTZ<5=yD>tm5QxhVPn}MuE-+etL@e!rF&)x!JwTxOX{u$6 zULX)Kn*GzZCkV8g9u>pz|QXE5;}g$Th}D5XddYXb{LH#)uHe9mc2-2pGrd`0#)+G6Xum zS;iV;bO_`EgAjk=_kb6DJ1Y!ATsOb<*aCwP_nBZ2;&H&Jdg+xA&#QVJoDi?8hCzt+ zs$dWzE_Dn-#HWfuh_vSIlm-SN(o>o15F)SQ7=*~L=yr+_+bXu5BEOMb#OwIEgORn<;%h#gigsAgwQJqgAlq!Z>I>MU({TO5OrF`AVj@hZl?%QzlU=j uLfCYNK?u99wo`<#?R*`a|GU9HV8}16vY(otGVFl>0000N;oC} literal 0 HcmV?d00001 diff --git a/src/plugins/home/public/index.ts b/src/plugins/home/public/index.ts index dc48332e052de..6079449554b08 100644 --- a/src/plugins/home/public/index.ts +++ b/src/plugins/home/public/index.ts @@ -24,6 +24,7 @@ export { EnvironmentSetup, TutorialSetup, HomePublicPluginSetup, + HomePublicPluginStart, } from './plugin'; export { FeatureCatalogueEntry, diff --git a/src/plugins/home/public/plugin.ts b/src/plugins/home/public/plugin.ts index 6859d916a61af..ec272c3f38399 100644 --- a/src/plugins/home/public/plugin.ts +++ b/src/plugins/home/public/plugin.ts @@ -42,6 +42,7 @@ import { TelemetryPluginStart } from '../../telemetry/public'; import { UsageCollectionSetup } from '../../usage_collection/public'; import { KibanaLegacySetup, KibanaLegacyStart } from '../../kibana_legacy/public'; import { AppNavLinkStatus } from '../../../core/public'; +import { PLUGIN_ID, HOME_APP_BASE_PATH } from '../common/constants'; export interface HomePluginStartDependencies { data: DataPublicPluginStart; @@ -56,7 +57,12 @@ export interface HomePluginSetupDependencies { export class HomePublicPlugin implements - Plugin { + Plugin< + HomePublicPluginSetup, + HomePublicPluginStart, + HomePluginSetupDependencies, + HomePluginStartDependencies + > { private readonly featuresCatalogueRegistry = new FeatureCatalogueRegistry(); private readonly environmentService = new EnvironmentService(); private readonly tutorialService = new TutorialService(); @@ -66,9 +72,9 @@ export class HomePublicPlugin public setup( core: CoreSetup, { kibanaLegacy, usageCollection }: HomePluginSetupDependencies - ): HomePublicPluginSetup { + ) { core.application.register({ - id: 'home', + id: PLUGIN_ID, title: 'Home', navLinkStatus: AppNavLinkStatus.hidden, mount: async (params: AppMountParameters) => { @@ -120,11 +126,9 @@ export class HomePublicPlugin { application: { capabilities, currentAppId$ }, http }: CoreStart, { kibanaLegacy }: HomePluginStartDependencies ) { - this.featuresCatalogueRegistry.start({ capabilities }); - // If the home app is the initial location when loading Kibana... if ( - window.location.pathname === http.basePath.prepend(`/app/home`) && + window.location.pathname === http.basePath.prepend(HOME_APP_BASE_PATH) && window.location.hash === '' ) { // ...wait for the app to mount initially and then... @@ -136,6 +140,8 @@ export class HomePublicPlugin } }); } + + return { featureCatalogue: { ...this.featuresCatalogueRegistry.start({ capabilities }) } }; } } @@ -149,13 +155,5 @@ export type EnvironmentSetup = EnvironmentServiceSetup; export type TutorialSetup = TutorialServiceSetup; /** @public */ -export interface HomePublicPluginSetup { - tutorials: TutorialServiceSetup; - featureCatalogue: FeatureCatalogueSetup; - /** - * The environment service is only available for a transition period and will - * be replaced by display specific extension points. - * @deprecated - */ - environment: EnvironmentSetup; -} +export type HomePublicPluginSetup = ReturnType; +export type HomePublicPluginStart = ReturnType; diff --git a/src/plugins/home/public/services/feature_catalogue/feature_catalogue_registry.ts b/src/plugins/home/public/services/feature_catalogue/feature_catalogue_registry.ts index c70fc0464b131..d4e9b35d5fa43 100644 --- a/src/plugins/home/public/services/feature_catalogue/feature_catalogue_registry.ts +++ b/src/plugins/home/public/services/feature_catalogue/feature_catalogue_registry.ts @@ -42,7 +42,7 @@ export interface FeatureCatalogueEntry { /** URL path to link to this future. Should not include the basePath. */ readonly path: string; /** Whether or not this link should be shown on the front page of Kibana. */ - readonly showOnHomePage: boolean; + showOnHomePage: boolean; } export class FeatureCatalogueRegistry { @@ -65,6 +65,22 @@ export class FeatureCatalogueRegistry { public start({ capabilities }: { capabilities: Capabilities }) { this.capabilities = capabilities; + return { + showOnHomePage: (featureId: string) => { + const feature = this.features.get(featureId); + if (feature) { + feature.showOnHomePage = true; + this.features.set(featureId, feature); + } + }, + hideFromHomePage: (featureId: string) => { + const feature = this.features.get(featureId); + if (feature) { + feature.showOnHomePage = false; + this.features.set(featureId, feature); + } + }, + }; } public get(): readonly FeatureCatalogueEntry[] { diff --git a/src/plugins/management/public/plugin.ts b/src/plugins/management/public/plugin.ts index 8be130b4028c6..93a5e36679c0d 100644 --- a/src/plugins/management/public/plugin.ts +++ b/src/plugins/management/public/plugin.ts @@ -56,7 +56,7 @@ export class ManagementPlugin implements Plugin { const config = this.initializerContext.config.get(); const pluginSetupDeps = plugins; + // TODO: Can this be removed now with homepage redesign work, or would it be a breaking change? APM is no longer displayed on the new home page design pluginSetupDeps.home.environment.update({ apmUi: true }); pluginSetupDeps.home.featureCatalogue.register(featureCatalogueEntry); diff --git a/x-pack/plugins/graph/public/plugin.ts b/x-pack/plugins/graph/public/plugin.ts index 5b2566ffab7c0..1c36d09069c6d 100644 --- a/x-pack/plugins/graph/public/plugin.ts +++ b/x-pack/plugins/graph/public/plugin.ts @@ -23,6 +23,7 @@ import { checkLicense } from '../common/check_license'; import { FeatureCatalogueCategory, HomePublicPluginSetup, + HomePublicPluginStart, } from '../../../../src/plugins/home/public'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public'; import { ConfigSchema } from '../config'; @@ -38,6 +39,7 @@ export interface GraphPluginStartDependencies { data: DataPublicPluginStart; savedObjects: SavedObjectsStart; kibanaLegacy: KibanaLegacyStart; + home?: HomePublicPluginStart; } export class GraphPlugin @@ -108,12 +110,20 @@ export class GraphPlugin }); } - start(core: CoreStart) { + start(core: CoreStart, { home }: GraphPluginStartDependencies) { if (this.licensing === null) { throw new Error('Start called before setup'); } this.licensing.license$.subscribe((license) => { - toggleNavLink(checkLicense(license), core.chrome.navLinks); + const licenseInformation = checkLicense(license); + toggleNavLink(licenseInformation, core.chrome.navLinks); + if (home) { + if (licenseInformation.showAppLink) { + home.featureCatalogue.showOnHomePage('graph'); + } else { + home.featureCatalogue.hideFromHomePage('graph'); + } + } }); } diff --git a/x-pack/plugins/index_lifecycle_management/public/plugin.tsx b/x-pack/plugins/index_lifecycle_management/public/plugin.tsx index 1d26aa53752a9..b93ce200ce19a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/plugin.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/plugin.tsx @@ -4,8 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import { i18n } from '@kbn/i18n'; import { CoreSetup, PluginInitializerContext } from 'src/core/public'; - +import { FeatureCatalogueCategory } from '../../../../src/plugins/home/public'; import { PLUGIN } from '../common/constants'; import { init as initHttp } from './application/services/http'; import { init as initDocumentation } from './application/services/documentation'; @@ -30,7 +31,7 @@ export class IndexLifecycleManagementPlugin { getStartServices, } = coreSetup; - const { usageCollection, management, indexManagement } = plugins; + const { usageCollection, management, indexManagement, home } = plugins; // Initialize services even if the app isn't mounted, because they're used by index management extensions. initHttp(http); @@ -67,6 +68,21 @@ export class IndexLifecycleManagementPlugin { }, }); + home.featureCatalogue.register({ + id: PLUGIN.ID, + title: i18n.translate('xpack.indexLifecycleManagement.featureCatalogueTitle', { + defaultMessage: 'Manage index lifecycles', + }), + description: i18n.translate('xpack.indexLifecycleManagement.featureCatalogueTitle', { + defaultMessage: + 'Attach a policy to automate when and how to transition an index through its lifecycle.', + }), + icon: 'indexSettings', // TODO: This is the same icon used for rollups in the feature catalogue. Do we need to pick a different one here? + path: '/app/management/data/index_lifecycle_management', + showOnHomePage: true, + category: FeatureCatalogueCategory.ADMIN, + }); + if (indexManagement) { addAllExtensions(indexManagement.extensionsService); } diff --git a/x-pack/plugins/index_lifecycle_management/public/types.ts b/x-pack/plugins/index_lifecycle_management/public/types.ts index 178884a7ee679..beb7753f32592 100644 --- a/x-pack/plugins/index_lifecycle_management/public/types.ts +++ b/x-pack/plugins/index_lifecycle_management/public/types.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; import { ManagementSetup } from '../../../../src/plugins/management/public'; import { IndexManagementPluginSetup } from '../../index_management/public'; @@ -12,6 +13,7 @@ export interface PluginsDependencies { usageCollection?: UsageCollectionSetup; management: ManagementSetup; indexManagement?: IndexManagementPluginSetup; + home: HomePublicPluginSetup; } export interface ClientConfigType { diff --git a/x-pack/plugins/infra/server/features.ts b/x-pack/plugins/infra/server/features.ts index fa228e03194a9..3b5080ccdd3fe 100644 --- a/x-pack/plugins/infra/server/features.ts +++ b/x-pack/plugins/infra/server/features.ts @@ -15,11 +15,11 @@ export const METRICS_FEATURE = { icon: 'metricsApp', navLinkId: 'metrics', app: ['infra', 'kibana'], - catalogue: ['infraops'], + catalogue: ['infraops', 'metrics'], privileges: { all: { app: ['infra', 'kibana'], - catalogue: ['infraops'], + catalogue: ['infraops', 'metrics'], api: ['infra', 'actions-read', 'actions-all', 'alerting-read', 'alerting-all'], savedObject: { all: ['infrastructure-ui-source', 'alert', 'action', 'action_task_params'], @@ -39,7 +39,7 @@ export const METRICS_FEATURE = { }, read: { app: ['infra', 'kibana'], - catalogue: ['infraops'], + catalogue: ['infraops', 'metrics'], api: ['infra', 'actions-read', 'actions-all', 'alerting-read', 'alerting-all'], savedObject: { all: ['alert', 'action', 'action_task_params'], @@ -67,11 +67,11 @@ export const LOGS_FEATURE = { icon: 'logsApp', navLinkId: 'logs', app: ['infra', 'kibana'], - catalogue: ['infralogging'], + catalogue: ['infralogging', 'logs'], privileges: { all: { app: ['infra', 'kibana'], - catalogue: ['infralogging'], + catalogue: ['infralogging', 'logs'], api: ['infra'], savedObject: { all: ['infrastructure-ui-source'], @@ -81,7 +81,7 @@ export const LOGS_FEATURE = { }, read: { app: ['infra', 'kibana'], - catalogue: ['infralogging'], + catalogue: ['infralogging', 'logs'], api: ['infra'], savedObject: { all: [], diff --git a/x-pack/plugins/ingest_manager/kibana.json b/x-pack/plugins/ingest_manager/kibana.json index ab0a2ba24ba66..7bbbcde027fd4 100644 --- a/x-pack/plugins/ingest_manager/kibana.json +++ b/x-pack/plugins/ingest_manager/kibana.json @@ -4,8 +4,8 @@ "server": true, "ui": true, "configPath": ["xpack", "ingestManager"], - "requiredPlugins": ["licensing", "data", "encryptedSavedObjects"], - "optionalPlugins": ["security", "features", "cloud", "usageCollection", "home"], + "requiredPlugins": ["licensing", "data", "encryptedSavedObjects", "home"], + "optionalPlugins": ["security", "features", "cloud", "usageCollection" ], "extraPublicDirs": ["common"], "requiredBundles": ["kibanaReact", "esUiShared"] } diff --git a/x-pack/plugins/ingest_manager/public/plugin.ts b/x-pack/plugins/ingest_manager/public/plugin.ts index fde4e93f8e39f..bab4140517dd5 100644 --- a/x-pack/plugins/ingest_manager/public/plugin.ts +++ b/x-pack/plugins/ingest_manager/public/plugin.ts @@ -13,9 +13,13 @@ import { import { i18n } from '@kbn/i18n'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public'; import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/plugins/data/public'; -import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; +import { + HomePublicPluginSetup, + FeatureCatalogueCategory, +} from '../../../../src/plugins/home/public'; import { LicensingPluginSetup } from '../../licensing/public'; import { PLUGIN_ID, CheckPermissionsResponse, PostIngestSetupResponse } from '../common'; +import { BASE_PATH } from './applications/ingest_manager/constants'; import { IngestManagerConfigType } from '../common/types'; import { setupRouteService, appRoutesService } from '../common'; @@ -95,6 +99,20 @@ export class IngestManagerPlugin deps.home.tutorials.registerDirectoryNotice(PLUGIN_ID, TutorialDirectoryNotice); deps.home.tutorials.registerDirectoryHeaderLink(PLUGIN_ID, TutorialDirectoryHeaderLink); deps.home.tutorials.registerModuleNotice(PLUGIN_ID, TutorialModuleNotice); + + deps.home.featureCatalogue.register({ + id: 'ingestManager', + title: i18n.translate('xpack.ingestManager.featureCatalogueTitle', { + defaultMessage: 'Manage ingest', + }), + description: i18n.translate('xpack.ingestManager.featureCatalogueTitle', { + defaultMessage: 'Management for Elastic Agents and integrations.', + }), + icon: 'logstashInput', + showOnHomePage: true, + path: BASE_PATH, + category: FeatureCatalogueCategory.ADMIN, // TODO: is the correct category for this plugin? + }); } return {}; diff --git a/x-pack/plugins/ingest_manager/server/plugin.ts b/x-pack/plugins/ingest_manager/server/plugin.ts index 69af475886bb9..a73ac8307f26a 100644 --- a/x-pack/plugins/ingest_manager/server/plugin.ts +++ b/x-pack/plugins/ingest_manager/server/plugin.ts @@ -176,10 +176,12 @@ export class IngestManagerPlugin icon: 'savedObjectsApp', navLinkId: PLUGIN_ID, app: [PLUGIN_ID, 'kibana'], + catalogue: ['ingestManager'], privileges: { all: { api: [`${PLUGIN_ID}-read`, `${PLUGIN_ID}-all`], app: [PLUGIN_ID, 'kibana'], + catalogue: ['ingestManager'], savedObject: { all: allSavedObjectTypes, read: [], @@ -189,6 +191,7 @@ export class IngestManagerPlugin read: { api: [`${PLUGIN_ID}-read`], app: [PLUGIN_ID, 'kibana'], + catalogue: ['ingestManager'], savedObject: { all: [], read: allSavedObjectTypes, diff --git a/x-pack/plugins/ml/common/types/capabilities.ts b/x-pack/plugins/ml/common/types/capabilities.ts index f2177b0a3572f..907b4ddc6824f 100644 --- a/x-pack/plugins/ml/common/types/capabilities.ts +++ b/x-pack/plugins/ml/common/types/capabilities.ts @@ -78,7 +78,6 @@ export function getPluginPrivileges() { management: { insightsAndAlerting: ['jobsListLink'], }, - catalogue: [PLUGIN_ID], savedObject: { all: [], read: ['index-pattern', 'dashboard', 'search', 'visualization'], @@ -90,11 +89,13 @@ export function getPluginPrivileges() { ...privilege, api: allMlCapabilitiesKeys.map((k) => `ml:${k}`), ui: allMlCapabilitiesKeys, + catalogue: [PLUGIN_ID, `${PLUGIN_ID}_file_data_visualizer`], }, user: { ...privilege, api: userMlCapabilitiesKeys.map((k) => `ml:${k}`), ui: userMlCapabilitiesKeys, + catalogue: [PLUGIN_ID], }, }; } diff --git a/x-pack/plugins/ml/public/register_feature.ts b/x-pack/plugins/ml/public/register_feature.ts index ca60de612c3d5..179f8ed3b0769 100644 --- a/x-pack/plugins/ml/public/register_feature.ts +++ b/x-pack/plugins/ml/public/register_feature.ts @@ -12,6 +12,9 @@ import { import { PLUGIN_ID } from '../common/constants/app'; export const registerFeature = (home: HomePublicPluginSetup) => { + // TODO: Can this be removed now with homepage redesign work, or does this constitute a breaking change? This is no longer necessary for the home plugin + // if file data visualizer can be registered as its own feature + // register ML for the kibana home screen. // so the file data visualizer appears to allow people to import data home.environment.update({ ml: true }); @@ -31,4 +34,20 @@ export const registerFeature = (home: HomePublicPluginSetup) => { showOnHomePage: true, category: FeatureCatalogueCategory.DATA, }); + + // TODO: is it okay to register this as a separate feature in the feature catalogue? + // register data visualizer so it appears on the Kibana home page + home.featureCatalogue.register({ + id: `${PLUGIN_ID}_file_data_visualizer`, + title: i18n.translate('xpack.ml.fileDataVisualizerTitle', { + defaultMessage: 'Upload a file', + }), + description: i18n.translate('xpack.ml.fileDataVisualizerDescription', { + defaultMessage: 'Import your own CSV, NDJSON, or log file', + }), + icon: 'importAction', + path: '/app/ml#/filedatavisualizer', + showOnHomePage: true, + category: FeatureCatalogueCategory.DATA, + }); }; diff --git a/x-pack/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts index 812db744d1bda..fb1abc868e965 100644 --- a/x-pack/plugins/ml/server/plugin.ts +++ b/x-pack/plugins/ml/server/plugin.ts @@ -86,7 +86,7 @@ export class MlServerPlugin implements Plugin { constructor(context: PluginInitializerContext) {} - public setup(core: CoreSetup) { + public setup(core: CoreSetup, plugins: SetupPlugins) { core.application.register({ id: 'observability-overview', title: 'Overview', @@ -41,6 +51,22 @@ export class Plugin implements PluginClass, plugins: SetupPlugins) { initTelemetry(plugins.usageCollection, APP_ID); - plugins.home.featureCatalogue.register({ - id: APP_ID, - title: i18n.translate('xpack.securitySolution.featureCatalogue.title', { - defaultMessage: 'Security', - }), - description: i18n.translate('xpack.securitySolution.featureCatalogue.description', { - defaultMessage: 'Explore security metrics and logs for events and alerts', - }), - icon: APP_ICON, - path: APP_OVERVIEW_PATH, - showOnHomePage: true, - category: FeatureCatalogueCategory.DATA, - }); + if (plugins.home) { + plugins.home.featureCatalogue.register({ + id: APP_ID, + title: i18n.translate('xpack.securitySolution.featureCatalogue.title', { + defaultMessage: 'Security', + }), + description: i18n.translate('xpack.securitySolution.featureCatalogue.description', { + defaultMessage: 'Protect & prevent', + }), + icon: APP_ICON, + path: APP_OVERVIEW_PATH, + showOnHomePage: true, + category: FeatureCatalogueCategory.DATA, + }); + } plugins.triggers_actions_ui.actionTypeRegistry.register(jiraActionType()); plugins.triggers_actions_ui.actionTypeRegistry.register(resilientActionType()); diff --git a/x-pack/plugins/snapshot_restore/public/plugin.ts b/x-pack/plugins/snapshot_restore/public/plugin.ts index b864e70708652..e1b04730bfa64 100644 --- a/x-pack/plugins/snapshot_restore/public/plugin.ts +++ b/x-pack/plugins/snapshot_restore/public/plugin.ts @@ -8,6 +8,10 @@ import { CoreSetup, PluginInitializerContext } from 'src/core/public'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; import { ManagementSetup } from '../../../../src/plugins/management/public'; +import { + HomePublicPluginSetup, + FeatureCatalogueCategory, +} from '../../../../src/plugins/home/public'; import { PLUGIN } from '../common/constants'; import { ClientConfigType } from './types'; @@ -20,6 +24,7 @@ import { UIM_APP_NAME } from './application/constants'; interface PluginsDependencies { usageCollection: UsageCollectionSetup; management: ManagementSetup; + home?: HomePublicPluginSetup; } export class SnapshotRestoreUIPlugin { @@ -33,7 +38,7 @@ export class SnapshotRestoreUIPlugin { public setup(coreSetup: CoreSetup, plugins: PluginsDependencies): void { const config = this.initializerContext.config.get(); const { http } = coreSetup; - const { management, usageCollection } = plugins; + const { home, management, usageCollection } = plugins; // Initialize services this.uiMetricService.setup(usageCollection); @@ -54,6 +59,23 @@ export class SnapshotRestoreUIPlugin { return await mountManagementSection(coreSetup, services, config, params); }, }); + + if (home) { + home.featureCatalogue.register({ + id: PLUGIN.id, + title: i18n.translate('xpack.snapshotRestore.featureCatalogueTitle', { + defaultMessage: 'Store & recover backups', + }), + description: i18n.translate('xpack.snapshotRestore.featureCatalogueDescription', { + defaultMessage: + 'Use repositories to snapshot and restore Elasticsearch indices and clusters.', + }), + icon: 'storage', + path: '/app/management/data/snapshot_restore', + showOnHomePage: true, + category: FeatureCatalogueCategory.ADMIN, + }); + } } public start() {}