diff --git a/.eslintrc.js b/.eslintrc.js index a0363e77e3596..27dacd51be6f2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1046,9 +1046,7 @@ module.exports = { */ { // typescript only for front and back end - files: [ - 'x-pack/plugins/{alerts,alerting_builtins,actions,task_manager,event_log}/**/*.{ts,tsx}', - ], + files: ['x-pack/plugins/{alerts,stack_alerts,actions,task_manager,event_log}/**/*.{ts,tsx}'], rules: { '@typescript-eslint/no-explicit-any': 'error', }, diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d21d6ad81a0c6..5dd41581914ed 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -137,6 +137,7 @@ /src/plugins/home/public @elastic/kibana-core-ui /src/plugins/home/server/*.ts @elastic/kibana-core-ui /src/plugins/home/server/services/ @elastic/kibana-core-ui +/src/plugins/kibana_overview/ @elastic/kibana-core-ui /x-pack/plugins/global_search_bar/ @elastic/kibana-core-ui #CC# /src/legacy/core_plugins/newsfeed @elastic/kibana-core-ui #CC# /src/legacy/server/sample_data/ @elastic/kibana-core-ui @@ -316,7 +317,7 @@ x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @elastic/kib #CC# /x-pack/legacy/plugins/alerting/ @elastic/kibana-alerting-services #CC# /x-pack/legacy/plugins/task_manager @elastic/kibana-alerting-services #CC# /x-pack/legacy/plugins/triggers_actions_ui/ @elastic/kibana-alerting-services -#CC# /x-pack/plugins/alerting_builtins @elastic/kibana-alerting-services +#CC# /x-pack/plugins/stack_alerts @elastic/kibana-alerting-services # Enterprise Search # Shared diff --git a/.i18nrc.json b/.i18nrc.json index 7ecaa89f66604..e0281b0a5bc21 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -29,6 +29,7 @@ "indexPatternManagement": "src/plugins/index_pattern_management", "advancedSettings": "src/plugins/advanced_settings", "kibana_legacy": "src/plugins/kibana_legacy", + "kibanaOverview": "src/plugins/kibana_overview", "kibana_react": "src/legacy/core_plugins/kibana_react", "kibana-react": "src/plugins/kibana_react", "kibana_utils": "src/plugins/kibana_utils", diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index d6d938bfc97de..21c51f8cabd32 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -110,6 +110,10 @@ in Kibana, e.g. visualizations. It has the form of a flyout panel. |This plugin contains several helpers and services to integrate pieces of the legacy Kibana app with the new Kibana platform. +|{kib-repo}blob/{branch}/src/plugins/kibana_overview/README.md[kibanaOverview] +|An overview page highlighting Kibana apps + + |{kib-repo}blob/{branch}/src/plugins/kibana_react/README.md[kibanaReact] |Tools for building React applications in Kibana. @@ -274,13 +278,6 @@ which will load the visualization's editor. |The Kibana actions plugin provides a framework to create executable actions. You can: -|{kib-repo}blob/{branch}/x-pack/plugins/alerting_builtins/README.md[alertingBuiltins] -|This plugin provides alertTypes shipped with Kibana for use with the -the alerts plugin. When enabled, it will register -the built-in alertTypes with the alerting plugin, register associated HTTP -routes, etc. - - |{kib-repo}blob/{branch}/x-pack/plugins/alerts/README.md[alerts] |The Kibana alerting plugin provides a common place to set up alerts. You can: @@ -402,7 +399,7 @@ the infrastructure monitoring use-case within Kibana. |{kib-repo}blob/{branch}/x-pack/plugins/ingest_manager/README.md[ingestManager] -|Fleet needs to have Elasticsearch API keys enabled, and also to have TLS enabled on kibana, (if you want to run Kibana without TLS you can provide the following config flag --xpack.ingestManager.fleet.tlsCheckDisabled=false) +|Fleet needs to have Elasticsearch API keys enabled, and also to have TLS enabled on kibana, (if you want to run Kibana without TLS you can provide the following config flag --xpack.fleet.agents.tlsCheckDisabled=false) |{kib-repo}blob/{branch}/x-pack/plugins/ingest_pipelines/README.md[ingestPipelines] @@ -486,6 +483,13 @@ using the CURL scripts in the scripts folder. |WARNING: Missing README. +|{kib-repo}blob/{branch}/x-pack/plugins/stack_alerts/README.md[stackAlerts] +|This plugin provides alertTypes shipped with Kibana for use with the +the alerts plugin. When enabled, it will register +the alertTypes by the Stack in the alerting plugin, register associated HTTP +routes, etc. + + |{kib-repo}blob/{branch}/x-pack/plugins/task_manager[taskManager] |WARNING: Missing README. diff --git a/docs/settings/ingest-manager-settings.asciidoc b/docs/settings/ingest-manager-settings.asciidoc index 30e11f726c26b..9fa83fc242b4a 100644 --- a/docs/settings/ingest-manager-settings.asciidoc +++ b/docs/settings/ingest-manager-settings.asciidoc @@ -7,7 +7,7 @@ experimental[] -You can configure `xpack.ingestManager` settings in your `kibana.yml`. +You can configure `xpack.fleet` settings in your `kibana.yml`. By default, {ingest-manager} is enabled. To use {fleet}, you also need to configure {kib} and {es} hosts. See the {ingest-guide}/index.html[Ingest Management] docs for more information. @@ -17,9 +17,9 @@ See the {ingest-guide}/index.html[Ingest Management] docs for more information. [cols="2*<"] |=== -| `xpack.ingestManager.enabled` {ess-icon} +| `xpack.fleet.enabled` {ess-icon} | Set to `true` (default) to enable {ingest-manager}. -| `xpack.ingestManager.fleet.enabled` {ess-icon} +| `xpack.fleet.agents.enabled` {ess-icon} | Set to `true` (default) to enable {fleet}. |=== @@ -29,7 +29,7 @@ See the {ingest-guide}/index.html[Ingest Management] docs for more information. [cols="2*<"] |=== -| `xpack.ingestManager.registryUrl` +| `xpack.fleet.registryUrl` | The address to use to reach {package-manager} registry. |=== @@ -37,11 +37,11 @@ See the {ingest-guide}/index.html[Ingest Management] docs for more information. [cols="2*<"] |=== -| `xpack.ingestManager.fleet.kibana.host` +| `xpack.fleet.agents.kibana.host` | The hostname used by {agent} for accessing {kib}. -| `xpack.ingestManager.fleet.elasticsearch.host` +| `xpack.fleet.agents.elasticsearch.host` | The hostname used by {agent} for accessing {es}. -| `xpack.ingestManager.fleet.tlsCheckDisabled` +| `xpack.fleet.agents.tlsCheckDisabled` | Set to `true` to allow {fleet} to run on a {kib} instance without TLS enabled. |=== diff --git a/docs/user/dashboard/dashboard.asciidoc b/docs/user/dashboard/dashboard.asciidoc index c8bff91be91a6..4fa4f9860c2bd 100644 --- a/docs/user/dashboard/dashboard.asciidoc +++ b/docs/user/dashboard/dashboard.asciidoc @@ -209,18 +209,18 @@ visualization, configure the customization options for your visualization. The data fields that are displayed are based on the selected <> and the <>. -To view the data fields in a different index pattern, click the index pattern, then select a new one. The data fields automatically update. +To view the data fields in a different index pattern, click the *Change Index Pattern* dropdown, then select a new one. To filter the data fields: -* Enter the name in the *Search field names*. -* Click *Field by type*, then select the filter. To show all fields in the index pattern, deselect *Only show fields with data*. +* Enter the data field name in *Search field names*. +* Click *Field filters*, then select the filter. [float] [[view-data-summaries]] -==== View data summaries +==== View data field summaries -To help you decide exactly the data you want to display, get a quick summary of each field. The summary shows the distribution of +To help you decide exactly the data you want to display, get a quick summary of each data field. The summary shows the distribution of values within the specified time range. To view the data field summary information, navigate to the field, then click *i*. @@ -252,11 +252,9 @@ When there is an exclamation point (!) next to a visualization type, *Lens* is u For each visualization type, you can customize the aggregation and labels. The options available depend on the selected visualization type. -. Click a data field name in the editor, or click *Drop a field here*. -. Change the options that appear. -+ -[role="screenshot"] -image::images/lens_aggregation_labels.png[Quick function options] +. From the editor, click a data field, or click *Drop a field or click to add*. + +. Change the options, then click *Close*. [float] [[add-layers-and-indices]] diff --git a/docs/user/dashboard/images/lens_aggregation_labels.png b/docs/user/dashboard/images/lens_aggregation_labels.png deleted file mode 100644 index 9dcf1d226a197..0000000000000 Binary files a/docs/user/dashboard/images/lens_aggregation_labels.png and /dev/null differ diff --git a/docs/user/dashboard/images/lens_drag_drop.gif b/docs/user/dashboard/images/lens_drag_drop.gif index ca62115e7ea3a..22939467daa12 100644 Binary files a/docs/user/dashboard/images/lens_drag_drop.gif and b/docs/user/dashboard/images/lens_drag_drop.gif differ diff --git a/docs/user/dashboard/images/lens_index_pattern.png b/docs/user/dashboard/images/lens_index_pattern.png index 90a34b7a5d225..0c89e7ab7f814 100644 Binary files a/docs/user/dashboard/images/lens_index_pattern.png and b/docs/user/dashboard/images/lens_index_pattern.png differ diff --git a/docs/user/dashboard/images/lens_layers.png b/docs/user/dashboard/images/lens_layers.png index 7410425a6977e..5bc4217b7fb7d 100644 Binary files a/docs/user/dashboard/images/lens_layers.png and b/docs/user/dashboard/images/lens_layers.png differ diff --git a/docs/user/dashboard/images/lens_suggestions.gif b/docs/user/dashboard/images/lens_suggestions.gif index 3258e924cb205..5ba6aa5dee14d 100644 Binary files a/docs/user/dashboard/images/lens_suggestions.gif and b/docs/user/dashboard/images/lens_suggestions.gif differ diff --git a/docs/user/dashboard/images/lens_tutorial_1.png b/docs/user/dashboard/images/lens_tutorial_1.png new file mode 100644 index 0000000000000..047701fa495a7 Binary files /dev/null and b/docs/user/dashboard/images/lens_tutorial_1.png differ diff --git a/docs/user/dashboard/images/lens_tutorial_2.png b/docs/user/dashboard/images/lens_tutorial_2.png new file mode 100644 index 0000000000000..c3e5992778985 Binary files /dev/null and b/docs/user/dashboard/images/lens_tutorial_2.png differ diff --git a/docs/user/dashboard/images/lens_tutorial_3.1.png b/docs/user/dashboard/images/lens_tutorial_3.1.png new file mode 100644 index 0000000000000..23d9491c315e4 Binary files /dev/null and b/docs/user/dashboard/images/lens_tutorial_3.1.png differ diff --git a/docs/user/dashboard/images/lens_tutorial_3.2.png b/docs/user/dashboard/images/lens_tutorial_3.2.png new file mode 100644 index 0000000000000..cfe10fa1acfcf Binary files /dev/null and b/docs/user/dashboard/images/lens_tutorial_3.2.png differ diff --git a/docs/user/dashboard/images/lens_tutorial_3.png b/docs/user/dashboard/images/lens_tutorial_3.png new file mode 100644 index 0000000000000..891f24334d720 Binary files /dev/null and b/docs/user/dashboard/images/lens_tutorial_3.png differ diff --git a/docs/user/dashboard/images/lens_viz_types.png b/docs/user/dashboard/images/lens_viz_types.png index 2ecfa6bd0e0e3..0060234667f4e 100644 Binary files a/docs/user/dashboard/images/lens_viz_types.png and b/docs/user/dashboard/images/lens_viz_types.png differ diff --git a/docs/user/dashboard/tutorials.asciidoc b/docs/user/dashboard/tutorials.asciidoc index 931720ccbe257..b04de5fd0da6f 100644 --- a/docs/user/dashboard/tutorials.asciidoc +++ b/docs/user/dashboard/tutorials.asciidoc @@ -6,13 +6,13 @@ Learn how to use *Lens*, *Vega*, and *Timelion* by going through one of the step [[lens-tutorial]] === Compare sales over time with Lens -Ready to create your own visualization with Lens? Use the following tutorial to create a visualization that lets you compare sales over time. +Ready to create your own visualization with *Lens*? Use the following tutorial to create a visualization that lets you compare sales over time. [float] [[lens-before-begin]] ==== Before you begin -To start, you'll need to add the <>. +To start, add the <>. [float] ==== Build the visualization @@ -23,16 +23,16 @@ Drag and drop your data onto the visualization builder pane. . Click image:images/time-filter-calendar.png[], then click *Last 7 days*. + -The fields in the data panel update. +The *Available fields* automatically update. . Drag and drop the *taxful_total_price* data field to the visualization builder pane. + [role="screenshot"] image::images/lens_tutorial_1.png[Lens tutorial] -To display the average order prices over time, *Lens* automatically added in *order_date* field. +To display the average order prices over time, *Lens* automatically added *order_date* to the *X-axis*. -To break down your data, drag the *category.keyword* field to the visualization builder pane. Lens +To break down your data, drag and drop the *category.keyword* field to the visualization builder pane. Lens knows that you want to show the top categories and compare them across the dates, and creates a chart that compares the sales for each of the top three categories: @@ -45,30 +45,33 @@ image::images/lens_tutorial_2.png[Lens tutorial] Make your visualization look exactly how you want with the customization options. -. Click *Average of taxful_total_price*, then change the *Label* to `Sales`. +. Click *Average of taxful_total_price*, then change the *Display name* to Sales. + [role="screenshot"] image::images/lens_tutorial_3.1.png[Lens tutorial] +. Click *Close*. + . Click *Top values of category.keyword*, then change *Number of values* to `10`. + [role="screenshot"] image::images/lens_tutorial_3.2.png[Lens tutorial] + +. Click *Close*. + The visualization updates to show there are only six available categories. -+ -Look at the *Suggestions*. An area chart is not an option, but for the sales data, a stacked area chart might be the best option. -. To switch the chart type, click *Stacked bar chart* in the column, then click *Stacked area* from the *Select a visualizations* window. +. Look at the *Suggestions*. An area chart is not an option, but for the sales data, a stacked area chart might be the best option. +To switch the chart type, click *Stacked bar chart*, then click *Stacked area* from the *Select a visualization* dropdown. + [role="screenshot"] image::images/lens_tutorial_3.png[Lens tutorial] [float] [[lens-tutorial-next-steps]] -==== Next steps +==== What's next? -Now that you've created your visualization, you can add it to a <> or <>. +Now that you've created your *Lens* visualization, add it to a <> or <>. [[vega-lite-tutorial-create-your-first-visualizations]] === Create your first visualization with Vega-Lite diff --git a/docs/visualize/images/lens_aggregation_labels.png b/docs/visualize/images/lens_aggregation_labels.png deleted file mode 100644 index 9dcf1d226a197..0000000000000 Binary files a/docs/visualize/images/lens_aggregation_labels.png and /dev/null differ diff --git a/docs/visualize/images/lens_data_info.png b/docs/visualize/images/lens_data_info.png deleted file mode 100644 index 5ea6fc64a217d..0000000000000 Binary files a/docs/visualize/images/lens_data_info.png and /dev/null differ diff --git a/docs/visualize/images/lens_drag_drop.gif b/docs/visualize/images/lens_drag_drop.gif deleted file mode 100644 index 1f8580d462702..0000000000000 Binary files a/docs/visualize/images/lens_drag_drop.gif and /dev/null differ diff --git a/docs/visualize/images/lens_index_pattern.png b/docs/visualize/images/lens_index_pattern.png deleted file mode 100644 index 90a34b7a5d225..0000000000000 Binary files a/docs/visualize/images/lens_index_pattern.png and /dev/null differ diff --git a/docs/visualize/images/lens_layers.png b/docs/visualize/images/lens_layers.png deleted file mode 100644 index 7410425a6977e..0000000000000 Binary files a/docs/visualize/images/lens_layers.png and /dev/null differ diff --git a/docs/visualize/images/lens_suggestions.gif b/docs/visualize/images/lens_suggestions.gif deleted file mode 100644 index 3258e924cb205..0000000000000 Binary files a/docs/visualize/images/lens_suggestions.gif and /dev/null differ diff --git a/docs/visualize/images/lens_tutorial_1.png b/docs/visualize/images/lens_tutorial_1.png deleted file mode 100644 index 77c1532e0ddac..0000000000000 Binary files a/docs/visualize/images/lens_tutorial_1.png and /dev/null differ diff --git a/docs/visualize/images/lens_tutorial_2.png b/docs/visualize/images/lens_tutorial_2.png deleted file mode 100644 index e7b8a7b515f52..0000000000000 Binary files a/docs/visualize/images/lens_tutorial_2.png and /dev/null differ diff --git a/docs/visualize/images/lens_tutorial_3.1.png b/docs/visualize/images/lens_tutorial_3.1.png deleted file mode 100644 index e9ed365e64aec..0000000000000 Binary files a/docs/visualize/images/lens_tutorial_3.1.png and /dev/null differ diff --git a/docs/visualize/images/lens_tutorial_3.2.png b/docs/visualize/images/lens_tutorial_3.2.png deleted file mode 100644 index c19bcb05dcb00..0000000000000 Binary files a/docs/visualize/images/lens_tutorial_3.2.png and /dev/null differ diff --git a/docs/visualize/images/lens_tutorial_3.png b/docs/visualize/images/lens_tutorial_3.png deleted file mode 100644 index 35fb10d4985e1..0000000000000 Binary files a/docs/visualize/images/lens_tutorial_3.png and /dev/null differ diff --git a/docs/visualize/images/lens_viz_types.png b/docs/visualize/images/lens_viz_types.png deleted file mode 100644 index 2ecfa6bd0e0e3..0000000000000 Binary files a/docs/visualize/images/lens_viz_types.png and /dev/null differ diff --git a/packages/kbn-telemetry-tools/src/tools/serializer.test.ts b/packages/kbn-telemetry-tools/src/tools/serializer.test.ts index 85fb84c714e20..48c4d73c291fe 100644 --- a/packages/kbn-telemetry-tools/src/tools/serializer.test.ts +++ b/packages/kbn-telemetry-tools/src/tools/serializer.test.ts @@ -148,6 +148,17 @@ describe('getDescriptor', () => { }); }); + it('serializes RecordWithKnownAllProps', () => { + const usageInterface = usageInterfaces.get('RecordWithKnownAllProps')!; + const descriptor = getDescriptor(usageInterface, tsProgram); + expect(descriptor).toEqual({ + prop1: { kind: ts.SyntaxKind.NumberKeyword, type: 'NumberKeyword' }, + prop2: { kind: ts.SyntaxKind.NumberKeyword, type: 'NumberKeyword' }, + prop3: { kind: ts.SyntaxKind.NumberKeyword, type: 'NumberKeyword' }, + prop4: { kind: ts.SyntaxKind.NumberKeyword, type: 'NumberKeyword' }, + }); + }); + it('serializes IndexedAccessType', () => { const usageInterface = usageInterfaces.get('IndexedAccessType')!; const descriptor = getDescriptor(usageInterface, tsProgram); diff --git a/packages/kbn-telemetry-tools/src/tools/serializer.ts b/packages/kbn-telemetry-tools/src/tools/serializer.ts index ea5f184008026..d380e17759e21 100644 --- a/packages/kbn-telemetry-tools/src/tools/serializer.ts +++ b/packages/kbn-telemetry-tools/src/tools/serializer.ts @@ -88,7 +88,11 @@ export function getConstraints(node: ts.Node, program: ts.Program): any { if (ts.isUnionTypeNode(node)) { const types = node.types.filter(discardNullOrUndefined); - return types.map((typeNode) => getConstraints(typeNode, program)); + return types.reduce((acc, typeNode) => { + const constraints = getConstraints(typeNode, program); + const contraintsArray = Array.isArray(constraints) ? constraints : [constraints]; + return [...acc, ...contraintsArray]; + }, []); } if (ts.isLiteralTypeNode(node) && ts.isLiteralExpression(node.literal)) { diff --git a/src/fixtures/telemetry_collectors/constants.ts b/src/fixtures/telemetry_collectors/constants.ts index 8896c294676c4..5b66161d07101 100644 --- a/src/fixtures/telemetry_collectors/constants.ts +++ b/src/fixtures/telemetry_collectors/constants.ts @@ -58,6 +58,10 @@ export type TypeAliasWithRecord = Usage & Record; export type MappedTypeProps = 'prop1' | 'prop2'; +export type MappedTypeExtraProps = 'prop3' | 'prop4'; + +export type MappedTypeAllProps = MappedTypeProps | MappedTypeExtraProps; + export interface MappedTypes { mappedTypeWithExternallyDefinedProps: { [key in MappedTypeProps]: number; @@ -68,5 +72,6 @@ export interface MappedTypes { } export type RecordWithKnownProps = Record; +export type RecordWithKnownAllProps = Record; export type IndexedAccessType = Pick; 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 8c9e3847844d9..afdd90959eabd 100644 --- a/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx +++ b/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx @@ -121,7 +121,18 @@ export class AdvancedSettingsComponent extends Component< setTimeout(() => { const id = hash.replace('#', ''); const element = document.getElementById(id); - const globalNavOffset = document.getElementById('globalHeaderBars')?.offsetHeight || 0; + + let globalNavOffset = 0; + + const globalNavBars = document + .getElementById('globalHeaderBars') + ?.getElementsByClassName('euiHeader'); + + if (globalNavBars) { + Array.from(globalNavBars).forEach((navBar) => { + globalNavOffset += (navBar as HTMLDivElement).offsetHeight; + }); + } if (element) { element.scrollIntoView(); diff --git a/src/plugins/dashboard/public/application/index.ts b/src/plugins/dashboard/public/application/index.ts index fcd92da33aa5f..2558c49648b10 100644 --- a/src/plugins/dashboard/public/application/index.ts +++ b/src/plugins/dashboard/public/application/index.ts @@ -19,4 +19,4 @@ export * from './embeddable'; export * from './actions'; -export { RenderDeps } from './application'; +export type { RenderDeps } from './application'; diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 91f603dfc6c77..3325d193e56ed 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -395,6 +395,9 @@ export class DashboardPlugin title: i18n.translate('dashboard.featureCatalogue.dashboardTitle', { defaultMessage: 'Dashboard', }), + subtitle: i18n.translate('dashboard.featureCatalogue.dashboardSubtitle', { + defaultMessage: 'Analyze data in dashboards.', + }), description: i18n.translate('dashboard.featureCatalogue.dashboardDescription', { defaultMessage: 'Display and share a collection of visualizations and saved searches.', }), @@ -402,6 +405,8 @@ export class DashboardPlugin path: `/app/dashboards#${DashboardConstants.LANDING_PAGE_PATH}`, showOnHomePage: false, category: FeatureCatalogueCategory.DATA, + solutionId: 'kibana', + order: 100, }); } } diff --git a/src/plugins/discover/public/register_feature.ts b/src/plugins/discover/public/register_feature.ts index 5443bb261ab10..9a66936233692 100644 --- a/src/plugins/discover/public/register_feature.ts +++ b/src/plugins/discover/public/register_feature.ts @@ -25,6 +25,9 @@ export function registerFeature(home: HomePublicPluginSetup) { title: i18n.translate('discover.discoverTitle', { defaultMessage: 'Discover', }), + subtitle: i18n.translate('discover.discoverSubtitle', { + defaultMessage: 'Search and find insights.', + }), description: i18n.translate('discover.discoverDescription', { defaultMessage: 'Interactively explore your data by querying and filtering raw documents.', }), @@ -32,5 +35,7 @@ export function registerFeature(home: HomePublicPluginSetup) { path: '/app/discover#/', showOnHomePage: false, category: FeatureCatalogueCategory.DATA, + solutionId: 'kibana', + order: 200, }); } diff --git a/src/plugins/home/public/application/components/__snapshots__/home.test.js.snap b/src/plugins/home/public/application/components/__snapshots__/home.test.js.snap index bf1e8c8f0b401..e9b0494105e12 100644 --- a/src/plugins/home/public/application/components/__snapshots__/home.test.js.snap +++ b/src/plugins/home/public/application/components/__snapshots__/home.test.js.snap @@ -2,57 +2,26 @@ exports[`home change home route should render a link to change the default route in advanced settings if advanced settings is enabled 1`] = `
-
-
- - - -

- -

-
-
- - - - - Add data - - - - -
-
-
+ + } + />
- 0 @@ -73,115 +42,36 @@ exports[`home change home route should render a link to change the default route aria-hidden="true" margin="xl" /> -
- - - - - - - - - - - - -
+
`; exports[`home directories should not render directory entry when showOnHomePage is false 1`] = `
-
-
- - - -

- -

-
-
- - - - - Add data - - - - - Manage - - - - -
-
-
+ + } + />
- 0 @@ -202,92 +92,36 @@ exports[`home directories should not render directory entry when showOnHomePage aria-hidden="true" margin="xl" /> -
- - - - - - - - -
+
`; exports[`home directories should render ADMIN directory entry in "Manage your data" panel 1`] = `
-
-
- - - -

- -

-
-
- - - - - Add data - - - - -
-
-
+ + } + />
- 0 @@ -320,92 +154,36 @@ exports[`home directories should render ADMIN directory entry in "Manage your da aria-hidden="true" margin="xl" /> -
- - - - - - - - -
+
`; exports[`home directories should render DATA directory entry in "Ingest your data" panel 1`] = `
-
-
- - - -

- -

-
-
- - - - - Add data - - - - -
-
-
+ + } + />
- 0 @@ -438,97 +216,43 @@ exports[`home directories should render DATA directory entry in "Ingest your dat aria-hidden="true" margin="xl" /> -
- - - - - - - - -
+
`; exports[`home directories should render solutions in the "solution section" 1`] = `
-
-
- - - -

- -

-
-
- - - - - Add data - - - - -
-
-
+ + } + />
`; exports[`home header render 1`] = `
-
-
- - - -

- -

-
-
- - - - - Add data - - - - -
-
-
+ + } + />
- 0 @@ -700,102 +368,36 @@ exports[`home header render 1`] = ` aria-hidden="true" margin="xl" /> -
- - - - - - - - -
+
`; exports[`home header should show "Dev tools" link if console is available 1`] = `
-
-
- - - -

- -

-
-
- - - - - Add data - - - - - Dev tools - - - - -
-
-
+ + } + />
- 0 @@ -828,103 +430,36 @@ exports[`home header should show "Dev tools" link if console is available 1`] = aria-hidden="true" margin="xl" /> -
- - - - - - - - -
+
`; exports[`home header should show "Manage" link if stack management is available 1`] = `
-
-
- - - -

- -

-
-
- - - - - Add data - - - - - Manage - - - - -
-
-
+ + } + />
- 0 @@ -945,92 +480,36 @@ exports[`home header should show "Manage" link if stack management is available aria-hidden="true" margin="xl" /> -
- - - - - - - - -
+
`; exports[`home isNewKibanaInstance should safely handle execeptions 1`] = `
-
-
- - - -

- -

-
-
- - - - - Add data - - - - -
-
-
+ + } + />
- 0 @@ -1051,92 +530,36 @@ exports[`home isNewKibanaInstance should safely handle execeptions 1`] = ` aria-hidden="true" margin="xl" /> -
- - - - - - - - -
+
`; exports[`home isNewKibanaInstance should set isNewKibanaInstance to false when there are index patterns 1`] = `
-
-
- - - -

- -

-
-
- - - - - Add data - - - - -
-
-
+ + } + />
- 0 @@ -1157,92 +580,36 @@ exports[`home isNewKibanaInstance should set isNewKibanaInstance to false when t aria-hidden="true" margin="xl" /> -
- - - - - - - - -
+
`; exports[`home isNewKibanaInstance should set isNewKibanaInstance to true when there are no index patterns 1`] = `
-
-
- - - -

- -

-
-
- - - - - Add data - - - - -
-
-
+ + } + />
- 0 @@ -1263,92 +630,36 @@ exports[`home isNewKibanaInstance should set isNewKibanaInstance to true when th aria-hidden="true" margin="xl" /> -
- - - - - - - - -
+
`; exports[`home should render home component 1`] = `
-
-
- - - -

- -

-
-
- - - - - Add data - - - - -
-
-
+ + } + />
- 0 @@ -1369,92 +680,36 @@ exports[`home should render home component 1`] = ` aria-hidden="true" margin="xl" /> -
- - - - - - - - -
+
`; exports[`home welcome should show the normal home page if loading fails 1`] = `
-
-
- - - -

- -

-
-
- - - - - Add data - - - - -
-
-
+ + } + />
- 0 @@ -1475,92 +730,36 @@ exports[`home welcome should show the normal home page if loading fails 1`] = ` aria-hidden="true" margin="xl" /> -
- - - - - - - - -
+
`; exports[`home welcome should show the normal home page if welcome screen is disabled locally 1`] = `
-
-
- - - -

- -

-
-
- - - - - Add data - - - - -
-
-
+ + } + />
- 0 @@ -1581,35 +780,10 @@ exports[`home welcome should show the normal home page if welcome screen is disa aria-hidden="true" margin="xl" /> -
- - - - - - - - -
+
`; @@ -1623,57 +797,26 @@ exports[`home welcome should show the welcome screen if enabled, and there are n exports[`home welcome stores skip welcome setting if skipped 1`] = `
-
-
- - - -

- -

-
-
- - - - - Add data - - - - -
-
-
+ + } + />
- 0 @@ -1694,35 +837,10 @@ exports[`home welcome stores skip welcome setting if skipped 1`] = ` aria-hidden="true" margin="xl" /> -
- - - - - - - - -
+
`; diff --git a/src/plugins/home/public/application/components/__snapshots__/synopsis.test.js.snap b/src/plugins/home/public/application/components/__snapshots__/synopsis.test.js.snap index 190985f70659d..ec192ce1eb32f 100644 --- a/src/plugins/home/public/application/components/__snapshots__/synopsis.test.js.snap +++ b/src/plugins/home/public/application/components/__snapshots__/synopsis.test.js.snap @@ -9,6 +9,7 @@ exports[`props iconType 1`] = ` href="link_to_item" icon={ - - - +
+ + + +
= ({ addBasePath, features }) => (
- +

@@ -43,18 +43,21 @@ export const AddData: FC = ({ addBasePath, features }) => ( - - - - + +
+ + + +
diff --git a/src/plugins/home/public/application/components/home.js b/src/plugins/home/public/application/components/home.js index becafb2560217..054f5a5344df4 100644 --- a/src/plugins/home/public/application/components/home.js +++ b/src/plugins/home/public/application/components/home.js @@ -20,18 +20,16 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from '@kbn/i18n/react'; -import { - EuiButtonEmpty, - EuiTitle, - EuiFlexGroup, - EuiFlexItem, - EuiHorizontalRule, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { + OverviewPageFooter, + OverviewPageHeader, +} from '../../../../../../src/plugins/kibana_react/public'; +import { HOME_APP_BASE_PATH } from '../../../common/constants'; import { FeatureCatalogueCategory } from '../../services'; import { getServices } from '../kibana_services'; import { AddData } from './add_data'; -import { createAppNavigationHandler } from './app_navigation_handler'; import { ManageData } from './manage_data'; import { SolutionsSection } from './solutions_section'; import { Welcome } from './welcome'; @@ -121,12 +119,9 @@ export class Home extends Component { .sort((directoryA, directoryB) => directoryA.order - directoryB.order); renderNormal() { - const { addBasePath, solutions } = this.props; + const { addBasePath, solutions, directories } = this.props; const devTools = this.findDirectoryById('console'); - const stackManagement = this.findDirectoryById('stack-management'); - const advancedSettings = this.findDirectoryById('advanced_settings'); - const addDataFeatures = this.getFeaturesByCategory(FeatureCatalogueCategory.DATA); const manageDataFeatures = this.getFeaturesByCategory(FeatureCatalogueCategory.ADMIN); @@ -136,68 +131,27 @@ export class Home extends Component { } return ( -
-
-
- - - -

- -

-
-
- - - - - - {i18n.translate('home.pageHeader.addDataButtonLabel', { - defaultMessage: 'Add data', - })} - - - - {stackManagement ? ( - - - {i18n.translate('home.pageHeader.stackManagementButtonLabel', { - defaultMessage: 'Manage', - })} - - - ) : null} - - {devTools ? ( - - - {i18n.translate('home.pageHeader.devToolsButtonLabel', { - defaultMessage: 'Dev tools', - })} - - - ) : null} - - -
-
-
+
+ } + />
- {solutions.length && } + {solutions.length ? ( + + ) : null}
); @@ -294,12 +214,14 @@ Home.propTypes = { PropTypes.shape({ id: PropTypes.string.isRequired, title: PropTypes.string.isRequired, + subtitle: PropTypes.string, description: PropTypes.string.isRequired, icon: PropTypes.string.isRequired, path: PropTypes.string.isRequired, showOnHomePage: PropTypes.bool.isRequired, category: PropTypes.string.isRequired, order: PropTypes.number, + solutionId: PropTypes.string, }) ), solutions: PropTypes.arrayOf( @@ -307,7 +229,8 @@ Home.propTypes = { id: PropTypes.string.isRequired, title: PropTypes.string.isRequired, subtitle: PropTypes.string.isRequired, - descriptions: PropTypes.arrayOf(PropTypes.string).isRequired, + description: PropTypes.string, + appDescriptions: PropTypes.arrayOf(PropTypes.string).isRequired, icon: PropTypes.string.isRequired, path: PropTypes.string.isRequired, order: PropTypes.number, diff --git a/src/plugins/home/public/application/components/home.test.js b/src/plugins/home/public/application/components/home.test.js index 0d7596d92a5a1..9c73bbf9b75ba 100644 --- a/src/plugins/home/public/application/components/home.test.js +++ b/src/plugins/home/public/application/components/home.test.js @@ -35,6 +35,11 @@ jest.mock('../kibana_services', () => ({ }), })); +jest.mock('../../../../../../src/plugins/kibana_react/public', () => ({ + OverviewPageFooter: jest.fn().mockReturnValue(<>), + OverviewPageHeader: jest.fn().mockReturnValue(<>), +})); + describe('home', () => { let defaultProps; @@ -142,7 +147,7 @@ describe('home', () => { id: 'kibana', title: 'Kibana', subtitle: 'Visualize & analyze', - descriptions: ['Analyze data in dashboards'], + appDescriptions: ['Analyze data in dashboards'], icon: 'logoKibana', path: 'kibana_landing_page', order: 1, @@ -151,7 +156,7 @@ describe('home', () => { id: 'solution-2', title: 'Solution two', subtitle: 'Subtitle for solution two', - descriptions: ['Example use case'], + appDescriptions: ['Example use case'], icon: 'empty', path: 'path-to-solution-two', order: 2, @@ -160,7 +165,7 @@ describe('home', () => { id: 'solution-3', title: 'Solution three', subtitle: 'Subtitle for solution three', - descriptions: ['Example use case'], + appDescriptions: ['Example use case'], icon: 'empty', path: 'path-to-solution-three', order: 3, @@ -169,7 +174,7 @@ describe('home', () => { id: 'solution-4', title: 'Solution four', subtitle: 'Subtitle for solution four', - descriptions: ['Example use case'], + appDescriptions: ['Example use case'], icon: 'empty', path: 'path-to-solution-four', order: 4, diff --git a/src/plugins/home/public/application/components/home_app.js b/src/plugins/home/public/application/components/home_app.js index 69cd68d553d03..7fe4f4351c35b 100644 --- a/src/plugins/home/public/application/components/home_app.js +++ b/src/plugins/home/public/application/components/home_app.js @@ -104,12 +104,14 @@ HomeApp.propTypes = { PropTypes.shape({ id: PropTypes.string.isRequired, title: PropTypes.string.isRequired, + subtitle: PropTypes.string, description: PropTypes.string.isRequired, icon: PropTypes.string.isRequired, path: PropTypes.string.isRequired, showOnHomePage: PropTypes.bool.isRequired, category: PropTypes.string.isRequired, order: PropTypes.number, + solutionId: PropTypes.string, }) ), solutions: PropTypes.arrayOf( @@ -117,7 +119,8 @@ HomeApp.propTypes = { id: PropTypes.string.isRequired, title: PropTypes.string.isRequired, subtitle: PropTypes.string.isRequired, - descriptions: PropTypes.arrayOf(PropTypes.string).isRequired, + description: PropTypes.string, + appDescriptions: PropTypes.arrayOf(PropTypes.string).isRequired, icon: PropTypes.string.isRequired, path: PropTypes.string.isRequired, order: PropTypes.number, diff --git a/src/plugins/home/public/application/components/manage_data/manage_data.test.tsx b/src/plugins/home/public/application/components/manage_data/manage_data.test.tsx index 0e86bf7dd3d84..18a58e86eaa3f 100644 --- a/src/plugins/home/public/application/components/manage_data/manage_data.test.tsx +++ b/src/plugins/home/public/application/components/manage_data/manage_data.test.tsx @@ -37,7 +37,6 @@ const mockFeatures = [ { category: 'admin', description: 'Control who has access and what tasks they can perform.', - homePageSection: 'manage_data', icon: 'securityApp', id: 'security', order: 600, @@ -48,7 +47,6 @@ const mockFeatures = [ { category: 'admin', description: 'Track the real-time health and performance of your deployment.', - homePageSection: 'manage_data', icon: 'monitoringApp', id: 'monitoring', order: 610, @@ -60,7 +58,6 @@ const mockFeatures = [ category: 'admin', description: 'Save snapshots to a backup repository, and restore to recover index and cluster state.', - homePageSection: 'manage_data', icon: 'storage', id: 'snapshot_restore', order: 630, @@ -71,7 +68,6 @@ const mockFeatures = [ { category: 'admin', description: 'Define lifecycle policies to automatically perform operations as an index ages.', - homePageSection: 'manage_data', icon: 'indexSettings', id: 'index_lifecycle_management', order: 640, diff --git a/src/plugins/home/public/application/components/solutions_section/__snapshots__/solution_panel.test.tsx.snap b/src/plugins/home/public/application/components/solutions_section/__snapshots__/solution_panel.test.tsx.snap index ad92aac67d51b..726d3dda4e9cc 100644 --- a/src/plugins/home/public/application/components/solutions_section/__snapshots__/solution_panel.test.tsx.snap +++ b/src/plugins/home/public/application/components/solutions_section/__snapshots__/solution_panel.test.tsx.snap @@ -13,6 +13,7 @@ exports[`SolutionPanel renders the solution panel for the given solution 1`] = ` onClick={[Function]} >

diff --git a/src/plugins/home/public/application/components/solutions_section/__snapshots__/solutions_section.test.tsx.snap b/src/plugins/home/public/application/components/solutions_section/__snapshots__/solutions_section.test.tsx.snap index 7015ebb40a71d..4052ffca9e56f 100644 --- a/src/plugins/home/public/application/components/solutions_section/__snapshots__/solutions_section.test.tsx.snap +++ b/src/plugins/home/public/application/components/solutions_section/__snapshots__/solutions_section.test.tsx.snap @@ -7,19 +7,15 @@ exports[`SolutionsSection only renders a spacer if no solutions are available 1` className="homSolutions" > - -

- -

-
+ +

- -

- -

-
+ +

- -

- -

-
+ +
- -

- -

-
+ +
- descriptions.map(getDescriptionText).reduce(addSpacersBetweenElementsReducer, []); +const getDescriptions = (appDescriptions: string[]) => + appDescriptions + .map(getDescriptionText) + .reduce(addSpacersBetweenElementsReducer, []); interface Props { addBasePath: (path: string) => string; solution: FeatureCatalogueSolution; + apps?: FeatureCatalogueEntry[]; } -export const SolutionPanel: FC = ({ addBasePath, solution }) => ( +export const SolutionPanel: FC = ({ addBasePath, solution, apps = [] }) => ( = ({ addBasePath, solution }) => ( href={addBasePath(solution.path)} onClick={createAppNavigationHandler(solution.path)} > - + = ({ addBasePath, solution }) => ( - {getDescriptions(solution.descriptions)} + {getDescriptions( + apps.length ? apps.map(({ subtitle = '' }) => subtitle) : solution.appDescriptions + )} diff --git a/src/plugins/home/public/application/components/solutions_section/solution_title.tsx b/src/plugins/home/public/application/components/solutions_section/solution_title.tsx index fb833a40807d2..a9874ff7ddbe7 100644 --- a/src/plugins/home/public/application/components/solutions_section/solution_title.tsx +++ b/src/plugins/home/public/application/components/solutions_section/solution_title.tsx @@ -45,7 +45,7 @@ export const SolutionTitle: FC = ({ title, subtitle, iconType }) => ( className="homSolutionPanel__icon" /> - +

{title}

diff --git a/src/plugins/home/public/application/components/solutions_section/solutions_section.test.tsx b/src/plugins/home/public/application/components/solutions_section/solutions_section.test.tsx index 17d721cc96c02..9ec5bf695b35c 100644 --- a/src/plugins/home/public/application/components/solutions_section/solutions_section.test.tsx +++ b/src/plugins/home/public/application/components/solutions_section/solutions_section.test.tsx @@ -20,12 +20,13 @@ import React from 'react'; import { shallow } from 'enzyme'; import { SolutionsSection } from './solutions_section'; +import { FeatureCatalogueCategory } from '../../../services'; const solutionEntry1 = { id: 'kibana', title: 'Kibana', subtitle: 'Visualize & analyze', - descriptions: ['Analyze data in dashboards'], + appDescriptions: ['Analyze data in dashboards'], icon: 'logoKibana', path: 'kibana_landing_page', order: 1, @@ -34,7 +35,8 @@ const solutionEntry2 = { id: 'solution-2', title: 'Solution two', subtitle: 'Subtitle for solution two', - descriptions: ['Example use case'], + description: 'Description for solution two', + appDescriptions: ['Example use case'], icon: 'empty', path: 'path-to-solution-two', order: 2, @@ -43,7 +45,8 @@ const solutionEntry3 = { id: 'solution-3', title: 'Solution three', subtitle: 'Subtitle for solution three', - descriptions: ['Example use case'], + description: 'Description for solution three', + appDescriptions: ['Example use case'], icon: 'empty', path: 'path-to-solution-three', order: 3, @@ -52,23 +55,64 @@ const solutionEntry4 = { id: 'solution-4', title: 'Solution four', subtitle: 'Subtitle for solution four', - descriptions: ['Example use case'], + description: 'Description for solution four', + appDescriptions: ['Example use case'], icon: 'empty', path: 'path-to-solution-four', order: 4, }; +const mockDirectories = [ + { + id: 'dashboard', + title: 'Dashboard', + description: 'Description of dashboard', + icon: 'dashboardApp', + path: 'dashboard_landing_page', + showOnHomePage: false, + category: FeatureCatalogueCategory.DATA, + }, + { + id: 'discover', + title: 'Discover', + description: 'Description of discover', + icon: 'discoverApp', + path: 'discover_landing_page', + showOnHomePage: false, + category: FeatureCatalogueCategory.DATA, + }, + { + id: 'canvas', + title: 'Canvas', + description: 'Description of canvas', + icon: 'canvasApp', + path: 'canvas_landing_page', + showOnHomePage: false, + category: FeatureCatalogueCategory.DATA, + }, +]; + const addBasePathMock = (path: string) => (path ? path : 'path'); describe('SolutionsSection', () => { test('only renders a spacer if no solutions are available', () => { - const component = shallow(); + const component = shallow( + + ); expect(component).toMatchSnapshot(); }); test('renders a single solution', () => { const component = shallow( - + ); expect(component).toMatchSnapshot(); }); @@ -78,6 +122,7 @@ describe('SolutionsSection', () => { ); expect(component).toMatchSnapshot(); @@ -87,6 +132,7 @@ describe('SolutionsSection', () => { ); expect(component).toMatchSnapshot(); diff --git a/src/plugins/home/public/application/components/solutions_section/solutions_section.tsx b/src/plugins/home/public/application/components/solutions_section/solutions_section.tsx index 1a78a6c71030a..13b70383147eb 100644 --- a/src/plugins/home/public/application/components/solutions_section/solutions_section.tsx +++ b/src/plugins/home/public/application/components/solutions_section/solutions_section.tsx @@ -19,16 +19,10 @@ import React, { FC } from 'react'; import PropTypes from 'prop-types'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiHorizontalRule, - EuiScreenReaderOnly, - EuiTitle, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiScreenReaderOnly } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { SolutionPanel } from './solution_panel'; -import { FeatureCatalogueSolution } from '../../../'; +import { FeatureCatalogueEntry, FeatureCatalogueSolution } from '../../../'; const sortByOrder = ( { order: orderA = 0 }: FeatureCatalogueSolution, @@ -38,25 +32,25 @@ const sortByOrder = ( interface Props { addBasePath: (path: string) => string; solutions: FeatureCatalogueSolution[]; + directories: FeatureCatalogueEntry[]; } -export const SolutionsSection: FC = ({ addBasePath, solutions }) => { +export const SolutionsSection: FC = ({ addBasePath, solutions, directories }) => { // Separate Kibana from other solutions const kibana = solutions.find(({ id }) => id === 'kibana'); + const kibanaApps = directories.filter(({ solutionId }) => solutionId === 'kibana'); solutions = solutions.sort(sortByOrder).filter(({ id }) => id !== 'kibana'); return ( <>
- -

- -

-
+

+ +

@@ -69,7 +63,13 @@ export const SolutionsSection: FC = ({ addBasePath, solutions }) => { ) : null} - {kibana ? : null} + {kibana ? ( + + ) : null}
@@ -79,12 +79,28 @@ export const SolutionsSection: FC = ({ addBasePath, solutions }) => { }; SolutionsSection.propTypes = { + addBasePath: PropTypes.func.isRequired, + directories: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + subtitle: PropTypes.string, + description: PropTypes.string.isRequired, + icon: PropTypes.string.isRequired, + path: PropTypes.string.isRequired, + showOnHomePage: PropTypes.bool.isRequired, + category: PropTypes.string.isRequired, + order: PropTypes.number, + solutionId: PropTypes.string, + }) + ), solutions: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.string.isRequired, title: PropTypes.string.isRequired, subtitle: PropTypes.string.isRequired, - descriptions: PropTypes.arrayOf(PropTypes.string).isRequired, + description: PropTypes.string, + appDescriptions: PropTypes.arrayOf(PropTypes.string).isRequired, icon: PropTypes.string.isRequired, path: PropTypes.string.isRequired, order: PropTypes.number, diff --git a/src/plugins/home/public/application/components/synopsis.js b/src/plugins/home/public/application/components/synopsis.js index fbe3bb3ed6769..0777c0db7210e 100644 --- a/src/plugins/home/public/application/components/synopsis.js +++ b/src/plugins/home/public/application/components/synopsis.js @@ -38,7 +38,7 @@ export function Synopsis({ if (iconUrl) { optionalImg = ; } else if (iconType) { - optionalImg = ; + optionalImg = ; } const classes = classNames('homSynopsis__card', { diff --git a/src/plugins/home/public/index.ts b/src/plugins/home/public/index.ts index 4459d187eead7..e4ca6fb1439e8 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.test.ts b/src/plugins/home/public/plugin.test.ts index 7b56c6ec89b77..f8ff7c95aae08 100644 --- a/src/plugins/home/public/plugin.test.ts +++ b/src/plugins/home/public/plugin.test.ts @@ -52,23 +52,6 @@ describe('HomePublicPlugin', () => { ); }); - test('registers kibana solution to feature catalogue', async () => { - const setup = await new HomePublicPlugin(mockInitializerContext).setup( - coreMock.createSetup() as any, - { - urlForwarding: urlForwardingPluginMock.createSetupContract(), - } - ); - expect(setup).toHaveProperty('featureCatalogue'); - expect(setup.featureCatalogue.registerSolution).toHaveBeenCalledTimes(1); - expect(setup.featureCatalogue.registerSolution).toHaveBeenCalledWith( - expect.objectContaining({ - icon: 'logoKibana', - id: 'kibana', - }) - ); - }); - test('wires up and returns registry', async () => { const setup = await new HomePublicPlugin(mockInitializerContext).setup( coreMock.createSetup() as any, diff --git a/src/plugins/home/public/plugin.ts b/src/plugins/home/public/plugin.ts index b62ceae3d0d37..90f2f939101cb 100644 --- a/src/plugins/home/public/plugin.ts +++ b/src/plugins/home/public/plugin.ts @@ -58,7 +58,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(); @@ -128,39 +133,6 @@ export class HomePublicPlugin order: 500, }); - featureCatalogue.registerSolution({ - id: 'kibana', - title: i18n.translate('home.kibana.featureCatalogue.title', { - defaultMessage: 'Kibana', - }), - subtitle: i18n.translate('home.kibana.featureCatalogue.subtitle', { - defaultMessage: 'Visualize & analyze', - }), - descriptions: [ - i18n.translate('home.kibana.featureCatalogueDescription1', { - defaultMessage: 'Analyze data in dashboards.', - }), - i18n.translate('home.kibana.featureCatalogueDescription2', { - defaultMessage: 'Search and find insights.', - }), - i18n.translate('home.kibana.featureCatalogueDescription3', { - defaultMessage: 'Design pixel-perfect reports.', - }), - i18n.translate('home.kibana.featureCatalogueDescription4', { - defaultMessage: 'Plot geographic data.', - }), - i18n.translate('home.kibana.featureCatalogueDescription5', { - defaultMessage: 'Model, predict, and detect.', - }), - i18n.translate('home.kibana.featureCatalogueDescription6', { - defaultMessage: 'Reveal patterns and relationships.', - }), - ], - icon: 'logoKibana', - path: '/app/dashboards', - order: 400, - }); - return { featureCatalogue, environment: { ...this.environmentService.setup() }, @@ -188,6 +160,8 @@ export class HomePublicPlugin } }); } + + return { featureCatalogue: this.featuresCatalogueRegistry }; } } @@ -212,3 +186,6 @@ export interface HomePublicPluginSetup { environment: EnvironmentSetup; } +export interface HomePublicPluginStart { + featureCatalogue: FeatureCatalogueRegistry; +} diff --git a/src/plugins/home/public/services/feature_catalogue/feature_catalogue_registry.mock.ts b/src/plugins/home/public/services/feature_catalogue/feature_catalogue_registry.mock.ts index 23f36cef89ee6..e1a415ba2d571 100644 --- a/src/plugins/home/public/services/feature_catalogue/feature_catalogue_registry.mock.ts +++ b/src/plugins/home/public/services/feature_catalogue/feature_catalogue_registry.mock.ts @@ -36,6 +36,7 @@ const createMock = (): jest.Mocked> => start: jest.fn(), get: jest.fn(() => []), getSolutions: jest.fn(() => []), + removeFeature: jest.fn(), }; service.setup.mockImplementation(createSetupMock); return service; diff --git a/src/plugins/home/public/services/feature_catalogue/feature_catalogue_registry.test.ts b/src/plugins/home/public/services/feature_catalogue/feature_catalogue_registry.test.ts index b9902e0b10fb1..b009041bbf15b 100644 --- a/src/plugins/home/public/services/feature_catalogue/feature_catalogue_registry.test.ts +++ b/src/plugins/home/public/services/feature_catalogue/feature_catalogue_registry.test.ts @@ -38,7 +38,7 @@ const KIBANA_SOLUTION: FeatureCatalogueSolution = { id: 'kibana', title: 'Kibana', subtitle: 'Visualize & analyze', - descriptions: ['Analyze data in dashboards.', 'Search and find insights.'], + appDescriptions: ['Analyze data in dashboards.', 'Search and find insights.'], icon: 'kibanaApp', path: `/app/home`, }; 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 d965042b65cef..845070da0db9f 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 @@ -35,6 +35,8 @@ export interface FeatureCatalogueEntry { readonly title: string; /** {@link FeatureCatalogueCategory} to display this feature in. */ readonly category: FeatureCatalogueCategory; + /** A tagline of feature displayed to the user. */ + readonly subtitle?: string; /** One-line description of feature displayed to the user. */ readonly description: string; /** EUI `IconType` for icon to be displayed to the user. EUI supports any known EUI icon, SVG URL, or ReactElement. */ @@ -47,6 +49,8 @@ export interface FeatureCatalogueEntry { readonly order?: number; /** Optional function to control visibility of this feature. */ readonly visible?: () => boolean; + /** Unique string identifier of the solution this feature belongs to */ + readonly solutionId?: string; } /** @public */ @@ -57,8 +61,10 @@ export interface FeatureCatalogueSolution { readonly title: string; /** The tagline of the solution displayed to the user. */ readonly subtitle: string; + /** One-line description of the solution displayed to the user. */ + readonly description?: string; /** A list of use cases for this solution displayed to the user. */ - readonly descriptions: string[]; + readonly appDescriptions: string[]; /** EUI `IconType` for icon to be displayed to the user. EUI supports any known EUI icon, SVG URL, or ReactElement. */ readonly icon: IconType; /** URL path to link to this future. Should not include the basePath. */ @@ -99,7 +105,7 @@ export class FeatureCatalogueRegistry { this.capabilities = capabilities; } - public get(): readonly FeatureCatalogueEntry[] { + public get(): FeatureCatalogueEntry[] { if (this.capabilities === null) { throw new Error('Catalogue entries are only available after start phase'); } @@ -112,7 +118,7 @@ export class FeatureCatalogueRegistry { .sort(compareByKey('title')); } - public getSolutions(): readonly FeatureCatalogueSolution[] { + public getSolutions(): FeatureCatalogueSolution[] { if (this.capabilities === null) { throw new Error('Catalogue entries are only available after start phase'); } @@ -121,6 +127,10 @@ export class FeatureCatalogueRegistry { .filter((solution) => capabilities.catalogue[solution.id] !== false) .sort(compareByKey('title')); } + + public removeFeature(appId: string) { + this.features.delete(appId); + } } export type FeatureCatalogueRegistrySetup = ReturnType; diff --git a/src/plugins/kibana_overview/README.md b/src/plugins/kibana_overview/README.md new file mode 100644 index 0000000000000..ad0cbfdf7013b --- /dev/null +++ b/src/plugins/kibana_overview/README.md @@ -0,0 +1,9 @@ +# kibana-overview + +> An overview page highlighting Kibana apps + +--- + +## Development + +See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment. diff --git a/src/plugins/kibana_overview/common/index.ts b/src/plugins/kibana_overview/common/index.ts new file mode 100644 index 0000000000000..3bdfbee1081ad --- /dev/null +++ b/src/plugins/kibana_overview/common/index.ts @@ -0,0 +1,23 @@ +/* + * 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 = 'kibanaOverview'; +export const PLUGIN_NAME = 'Overview'; +export const PLUGIN_PATH = `/app/kibana_overview`; +export const PLUGIN_ICON = 'logoKibana'; diff --git a/src/plugins/kibana_overview/kibana.json b/src/plugins/kibana_overview/kibana.json new file mode 100644 index 0000000000000..9ddcaabdaed6b --- /dev/null +++ b/src/plugins/kibana_overview/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "kibanaOverview", + "version": "kibana", + "server": false, + "ui": true, + "requiredPlugins": ["navigation", "data", "home"], + "optionalPlugins": ["newsfeed"], + "requiredBundles": ["kibanaReact", "newsfeed"] +} diff --git a/src/plugins/kibana_overview/public/application.tsx b/src/plugins/kibana_overview/public/application.tsx new file mode 100644 index 0000000000000..1bf3fe07c36a8 --- /dev/null +++ b/src/plugins/kibana_overview/public/application.tsx @@ -0,0 +1,62 @@ +/* + * 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 ReactDOM from 'react-dom'; +import { I18nProvider } from '@kbn/i18n/react'; +import { KibanaContextProvider } from '../../../../src/plugins/kibana_react/public'; +import { NewsfeedApiEndpoint } from '../../../../src/plugins/newsfeed/public'; +import { AppMountParameters, CoreStart } from '../../../../src/core/public'; +import { AppPluginStartDependencies } from './types'; +import { KibanaOverviewApp } from './components/app'; + +export const renderApp = ( + core: CoreStart, + deps: AppPluginStartDependencies, + { appBasePath, element }: AppMountParameters +) => { + const { notifications, http } = core; + const { newsfeed, home, navigation } = deps; + const newsfeed$ = newsfeed?.createNewsFeed$(NewsfeedApiEndpoint.KIBANA_ANALYTICS); + const navLinks = core.chrome.navLinks.getAll(); + const solutions = home.featureCatalogue + .getSolutions() + .filter(({ id }) => id !== 'kibana') + .filter(({ id }) => navLinks.find(({ category, hidden }) => !hidden && category?.id === id)); + const features = home.featureCatalogue.get(); + + ReactDOM.render( + + + + + , + element + ); + + return () => ReactDOM.unmountComponentAtNode(element); +}; diff --git a/src/plugins/kibana_overview/public/assets/kibana_canvas_dark.svg b/src/plugins/kibana_overview/public/assets/kibana_canvas_dark.svg new file mode 100644 index 0000000000000..c86a3a89924c8 --- /dev/null +++ b/src/plugins/kibana_overview/public/assets/kibana_canvas_dark.svg @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/kibana_overview/public/assets/kibana_canvas_light.svg b/src/plugins/kibana_overview/public/assets/kibana_canvas_light.svg new file mode 100644 index 0000000000000..d51560cb915c9 --- /dev/null +++ b/src/plugins/kibana_overview/public/assets/kibana_canvas_light.svg @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/kibana_overview/public/assets/kibana_dashboard_dark.svg b/src/plugins/kibana_overview/public/assets/kibana_dashboard_dark.svg new file mode 100644 index 0000000000000..834dd98d60e4c --- /dev/null +++ b/src/plugins/kibana_overview/public/assets/kibana_dashboard_dark.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/kibana_overview/public/assets/kibana_dashboard_light.svg b/src/plugins/kibana_overview/public/assets/kibana_dashboard_light.svg new file mode 100644 index 0000000000000..958d25362c439 --- /dev/null +++ b/src/plugins/kibana_overview/public/assets/kibana_dashboard_light.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/kibana_overview/public/assets/kibana_discover_dark.svg b/src/plugins/kibana_overview/public/assets/kibana_discover_dark.svg new file mode 100644 index 0000000000000..cf3116ae7f36a --- /dev/null +++ b/src/plugins/kibana_overview/public/assets/kibana_discover_dark.svg @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/kibana_overview/public/assets/kibana_discover_light.svg b/src/plugins/kibana_overview/public/assets/kibana_discover_light.svg new file mode 100644 index 0000000000000..6e039d03bef89 --- /dev/null +++ b/src/plugins/kibana_overview/public/assets/kibana_discover_light.svg @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/kibana_overview/public/assets/kibana_graph_dark.svg b/src/plugins/kibana_overview/public/assets/kibana_graph_dark.svg new file mode 100644 index 0000000000000..ea43adf3390d0 --- /dev/null +++ b/src/plugins/kibana_overview/public/assets/kibana_graph_dark.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/kibana_overview/public/assets/kibana_graph_light.svg b/src/plugins/kibana_overview/public/assets/kibana_graph_light.svg new file mode 100644 index 0000000000000..c4505209a20bd --- /dev/null +++ b/src/plugins/kibana_overview/public/assets/kibana_graph_light.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/kibana_overview/public/assets/kibana_maps_dark.svg b/src/plugins/kibana_overview/public/assets/kibana_maps_dark.svg new file mode 100644 index 0000000000000..a6c53012a5e90 --- /dev/null +++ b/src/plugins/kibana_overview/public/assets/kibana_maps_dark.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/kibana_overview/public/assets/kibana_maps_light.svg b/src/plugins/kibana_overview/public/assets/kibana_maps_light.svg new file mode 100644 index 0000000000000..06a3ee5177eb9 --- /dev/null +++ b/src/plugins/kibana_overview/public/assets/kibana_maps_light.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/kibana_overview/public/assets/kibana_ml_dark.svg b/src/plugins/kibana_overview/public/assets/kibana_ml_dark.svg new file mode 100644 index 0000000000000..574760e523f51 --- /dev/null +++ b/src/plugins/kibana_overview/public/assets/kibana_ml_dark.svg @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/kibana_overview/public/assets/kibana_ml_light.svg b/src/plugins/kibana_overview/public/assets/kibana_ml_light.svg new file mode 100644 index 0000000000000..2375d78ce61f3 --- /dev/null +++ b/src/plugins/kibana_overview/public/assets/kibana_ml_light.svg @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/kibana_overview/public/assets/kibana_montage_dark.svg b/src/plugins/kibana_overview/public/assets/kibana_montage_dark.svg new file mode 100644 index 0000000000000..7a476e4b19ae1 --- /dev/null +++ b/src/plugins/kibana_overview/public/assets/kibana_montage_dark.svg @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/kibana_overview/public/assets/kibana_montage_light.svg b/src/plugins/kibana_overview/public/assets/kibana_montage_light.svg new file mode 100644 index 0000000000000..83f48a0d31a0c --- /dev/null +++ b/src/plugins/kibana_overview/public/assets/kibana_montage_light.svg @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/kibana_overview/public/assets/solutions_enterprise_search_dark_2x.png b/src/plugins/kibana_overview/public/assets/solutions_enterprise_search_dark_2x.png new file mode 100644 index 0000000000000..86ac827f06a77 Binary files /dev/null and b/src/plugins/kibana_overview/public/assets/solutions_enterprise_search_dark_2x.png differ diff --git a/src/plugins/kibana_overview/public/assets/solutions_enterprise_search_light_2x.png b/src/plugins/kibana_overview/public/assets/solutions_enterprise_search_light_2x.png new file mode 100644 index 0000000000000..527a09aad05ec Binary files /dev/null and b/src/plugins/kibana_overview/public/assets/solutions_enterprise_search_light_2x.png differ diff --git a/src/plugins/kibana_overview/public/assets/solutions_observability_dark_2x.png b/src/plugins/kibana_overview/public/assets/solutions_observability_dark_2x.png new file mode 100644 index 0000000000000..c9dd85ee07f35 Binary files /dev/null and b/src/plugins/kibana_overview/public/assets/solutions_observability_dark_2x.png differ diff --git a/src/plugins/kibana_overview/public/assets/solutions_observability_light_2x.png b/src/plugins/kibana_overview/public/assets/solutions_observability_light_2x.png new file mode 100644 index 0000000000000..85120b906c967 Binary files /dev/null and b/src/plugins/kibana_overview/public/assets/solutions_observability_light_2x.png differ diff --git a/src/plugins/kibana_overview/public/assets/solutions_security_solution_dark_2x.png b/src/plugins/kibana_overview/public/assets/solutions_security_solution_dark_2x.png new file mode 100644 index 0000000000000..24f902bff090b Binary files /dev/null and b/src/plugins/kibana_overview/public/assets/solutions_security_solution_dark_2x.png differ diff --git a/src/plugins/kibana_overview/public/assets/solutions_security_solution_light_2x.png b/src/plugins/kibana_overview/public/assets/solutions_security_solution_light_2x.png new file mode 100644 index 0000000000000..2b35af848f912 Binary files /dev/null and b/src/plugins/kibana_overview/public/assets/solutions_security_solution_light_2x.png differ diff --git a/src/plugins/kibana_overview/public/components/_index.scss b/src/plugins/kibana_overview/public/components/_index.scss new file mode 100644 index 0000000000000..b8857d171728f --- /dev/null +++ b/src/plugins/kibana_overview/public/components/_index.scss @@ -0,0 +1 @@ +@import 'overview'; diff --git a/src/plugins/kibana_overview/public/components/_overview.scss b/src/plugins/kibana_overview/public/components/_overview.scss new file mode 100644 index 0000000000000..74a58122d4851 --- /dev/null +++ b/src/plugins/kibana_overview/public/components/_overview.scss @@ -0,0 +1,120 @@ +/* + * 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. + */ + +.kbnOverviewWrapper { + background-color: $euiColorEmptyShade; + display: flex; + flex-direction: column; + min-height: calc(100vh - #{$euiHeaderHeightCompensation}); +} + +.kbnOverviewContent { + margin: 0 auto; + max-width: 1200px; + padding: $euiSizeXL $euiSize; + width: 100%; + + // Ensure card heights are stretched equally when wrapped with this element + .kbnRedirectCrossAppLinks { + align-items: flex-start; + display: flex; + flex: 1; + flex-direction: column; + } +} + +.kbnOverviewApps__item { + .kbnOverviewApps__group--primary & { + @include euiBreakpoint('m', 'l', 'xl') { + max-width: calc(50% - #{$euiSizeM * 2}); + } + } + + .kbnOverviewApps__group--secondary & { + @include euiBreakpoint('m', 'l', 'xl') { + max-width: calc(25% - #{$euiSizeM * 2}); + } + } +} + +.kbnOverviewNews__content article { + & + article { + margin-top: $euiSizeL; + } + + &, + header { + & > * + * { + margin-top: $euiSizeXS; + } + } + + h3 { + font-weight: inherit; + } +} + +.kbnOverviewMore__item { + @include euiBreakpoint('m', 'l', 'xl') { + max-width: calc(33.333333333333333% - #{$euiSizeM * 2}); + } +} + +.kbnOverviewSolution__icon { + background-color: $euiColorEmptyShade !important; + box-shadow: none !important; + height: $euiSizeL * 2; + padding: $euiSizeM; + width: $euiSizeL * 2; +} + +.kbnOverviewSupplements--noNews .kbnOverviewMore { + h2 { + @include euiBreakpoint('m', 'l', 'xl') { + text-align: center; + } + } + + .kbnOverviewMore__content { + @include euiBreakpoint('m', 'l', 'xl') { + justify-content: center; + } + } +} + +.kbnOverviewData--expanded { + flex-direction: column; + + &, + & > * { + margin-bottom: 0 !important; + margin-top: 0 !important; + } +} + +// Accounting for no `flush="both"` prop on EuiButtonEmpty +.kbnOverviewDataAdd__actionButton { + margin-right: 0; +} + +.kbnOverviewDataManage__item:not(:only-child) { + @include euiBreakpoint('m', 'l', 'xl') { + flex: 0 0 calc(50% - #{$euiSizeM * 2}); + } +} diff --git a/src/plugins/kibana_overview/public/components/add_data/__snapshots__/add_data.test.tsx.snap b/src/plugins/kibana_overview/public/components/add_data/__snapshots__/add_data.test.tsx.snap new file mode 100644 index 0000000000000..42623abd79ac0 --- /dev/null +++ b/src/plugins/kibana_overview/public/components/add_data/__snapshots__/add_data.test.tsx.snap @@ -0,0 +1,102 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AddData render 1`] = ` +
+ + + +

+ +

+
+
+ +
+ + + +
+
+
+ + + + + + + + + + + + + + + + + + +
+`; diff --git a/src/plugins/kibana_overview/public/components/add_data/add_data.test.tsx b/src/plugins/kibana_overview/public/components/add_data/add_data.test.tsx new file mode 100644 index 0000000000000..f5cdbd9e27e28 --- /dev/null +++ b/src/plugins/kibana_overview/public/components/add_data/add_data.test.tsx @@ -0,0 +1,67 @@ +/* + * 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 { FeatureCatalogueCategory } from 'src/plugins/home/public'; + +const mockFeatures = [ + { + category: FeatureCatalogueCategory.DATA, + description: 'Ingest data from popular apps and services.', + showOnHomePage: true, + icon: 'indexOpen', + id: 'home_tutorial_directory', + order: 500, + path: '/app/home#/tutorial_directory', + title: 'Ingest data', + }, + { + category: FeatureCatalogueCategory.ADMIN, + description: 'Add and manage your fleet of Elastic Agents and integrations.', + showOnHomePage: true, + icon: 'indexManagementApp', + id: 'ingestManager', + order: 510, + path: '/app/ingestManager', + title: 'Add Elastic Agent', + }, + { + category: FeatureCatalogueCategory.DATA, + description: 'Import your own CSV, NDJSON, or log file', + showOnHomePage: true, + icon: 'document', + id: 'ml_file_data_visualizer', + order: 520, + path: '/app/ml#/filedatavisualizer', + title: 'Upload a file', + }, +]; + +const addBasePathMock = jest.fn((path: string) => (path ? path : 'path')); + +describe('AddData', () => { + test('render', () => { + const component = shallowWithIntl( + + ); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/kibana_overview/public/components/add_data/add_data.tsx b/src/plugins/kibana_overview/public/components/add_data/add_data.tsx new file mode 100644 index 0000000000000..e29c2a08395cf --- /dev/null +++ b/src/plugins/kibana_overview/public/components/add_data/add_data.tsx @@ -0,0 +1,108 @@ +/* + * 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, { FC } from 'react'; +import PropTypes from 'prop-types'; +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { CoreStart } from 'kibana/public'; +import { RedirectAppLinks, useKibana } from '../../../../../../src/plugins/kibana_react/public'; +import { FeatureCatalogueEntry } from '../../../../../../src/plugins/home/public'; +// @ts-expect-error untyped component +import { Synopsis } from '../synopsis'; + +interface Props { + addBasePath: (path: string) => string; + features: FeatureCatalogueEntry[]; +} + +export const AddData: FC = ({ addBasePath, features }) => { + const { + services: { application }, + } = useKibana(); + + return ( +
+ + + +

+ +

+
+
+ + +
+ + + +
+
+
+ + + + + {features.map((feature) => ( + + + + + + ))} + +
+ ); +}; + +AddData.propTypes = { + addBasePath: PropTypes.func.isRequired, + features: 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, + order: PropTypes.number, + }) + ), +}; diff --git a/src/plugins/home/public/application/components/_manage_data.scss b/src/plugins/kibana_overview/public/components/add_data/index.ts similarity index 90% rename from src/plugins/home/public/application/components/_manage_data.scss rename to src/plugins/kibana_overview/public/components/add_data/index.ts index 389d8c8b3bf0f..a7d465d177636 100644 --- a/src/plugins/home/public/application/components/_manage_data.scss +++ b/src/plugins/kibana_overview/public/components/add_data/index.ts @@ -17,6 +17,4 @@ * under the License. */ -.homDataManage__content .euiIcon__fillSecondary { - fill: $euiColorDarkestShade; -} +export * from './add_data'; diff --git a/src/plugins/kibana_overview/public/components/app.tsx b/src/plugins/kibana_overview/public/components/app.tsx new file mode 100644 index 0000000000000..bf9211f49a18e --- /dev/null +++ b/src/plugins/kibana_overview/public/components/app.tsx @@ -0,0 +1,69 @@ +/* + * 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, { useEffect, useState } from 'react'; +import { Observable } from 'rxjs'; +import { I18nProvider } from '@kbn/i18n/react'; +import { HashRouter as Router, Switch, Route } from 'react-router-dom'; +import { CoreStart } from 'src/core/public'; +import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; +import { FetchResult } from 'src/plugins/newsfeed/public'; +import { FeatureCatalogueEntry, FeatureCatalogueSolution } from 'src/plugins/home/public'; +import { Overview } from './overview'; + +interface KibanaOverviewAppDeps { + basename: string; + notifications: CoreStart['notifications']; + http: CoreStart['http']; + navigation: NavigationPublicPluginStart; + newsfeed$?: Observable; + solutions: FeatureCatalogueSolution[]; + features: FeatureCatalogueEntry[]; +} + +export const KibanaOverviewApp = ({ + basename, + newsfeed$, + solutions, + features, +}: KibanaOverviewAppDeps) => { + const [newsFetchResult, setNewsFetchResult] = useState(null); + + useEffect(() => { + if (newsfeed$) { + const subscription = newsfeed$.subscribe((res: FetchResult | void | null) => { + setNewsFetchResult(res); + }); + + return () => subscription.unsubscribe(); + } + }, [newsfeed$]); + + return ( + + + + + + + + + + ); +}; diff --git a/src/plugins/kibana_overview/public/components/getting_started/__snapshots__/getting_started.test.tsx.snap b/src/plugins/kibana_overview/public/components/getting_started/__snapshots__/getting_started.test.tsx.snap new file mode 100644 index 0000000000000..374715a277ebc --- /dev/null +++ b/src/plugins/kibana_overview/public/components/getting_started/__snapshots__/getting_started.test.tsx.snap @@ -0,0 +1,391 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`GettingStarted dark mode on 1`] = ` +
+ + +
+ +

+ +

+
+ + +

+ +

+
+ + + + + } + layout="horizontal" + paddingSize="none" + title="Dashboard" + titleElement="h3" + titleSize="xs" + /> + + + + } + layout="horizontal" + paddingSize="none" + title="Discover" + titleElement="h3" + titleSize="xs" + /> + + + + } + layout="horizontal" + paddingSize="none" + title="Canvas" + titleElement="h3" + titleSize="xs" + /> + + + + } + layout="horizontal" + paddingSize="none" + title="Maps" + titleElement="h3" + titleSize="xs" + /> + + + + } + layout="horizontal" + paddingSize="none" + title="Machine Learning" + titleElement="h3" + titleSize="xs" + /> + + + + } + layout="horizontal" + paddingSize="none" + title="Graph" + titleElement="h3" + titleSize="xs" + /> + + + + + + + + +
+
+ + + +
+
+`; + +exports[`GettingStarted render 1`] = ` +
+ + +
+ +

+ +

+
+ + +

+ +

+
+ + + + + } + layout="horizontal" + paddingSize="none" + title="Dashboard" + titleElement="h3" + titleSize="xs" + /> + + + + } + layout="horizontal" + paddingSize="none" + title="Discover" + titleElement="h3" + titleSize="xs" + /> + + + + } + layout="horizontal" + paddingSize="none" + title="Canvas" + titleElement="h3" + titleSize="xs" + /> + + + + } + layout="horizontal" + paddingSize="none" + title="Maps" + titleElement="h3" + titleSize="xs" + /> + + + + } + layout="horizontal" + paddingSize="none" + title="Machine Learning" + titleElement="h3" + titleSize="xs" + /> + + + + } + layout="horizontal" + paddingSize="none" + title="Graph" + titleElement="h3" + titleSize="xs" + /> + + + + + + + + +
+
+ + + +
+
+`; diff --git a/src/plugins/kibana_overview/public/components/getting_started/getting_started.test.tsx b/src/plugins/kibana_overview/public/components/getting_started/getting_started.test.tsx new file mode 100644 index 0000000000000..7d40c4174f39b --- /dev/null +++ b/src/plugins/kibana_overview/public/components/getting_started/getting_started.test.tsx @@ -0,0 +1,118 @@ +/* + * 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 { GettingStarted } from './getting_started'; +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import { FeatureCatalogueCategory } from 'src/plugins/home/public'; + +const addBasePathMock = jest.fn((path: string) => (path ? path : 'path')); + +const mockApps = [ + { + category: FeatureCatalogueCategory.DATA, + description: 'Display and share a collection of visualizations and saved searches.', + icon: 'dashboardApp', + id: 'dashboard', + order: 100, + path: 'path-to-dashboard', + showOnHomePage: false, + solutionId: 'kibana', + subtitle: 'Analyze data in dashboards.', + title: 'Dashboard', + }, + { + category: FeatureCatalogueCategory.DATA, + description: 'Interactively explore your data by querying and filtering raw documents.', + icon: 'discoverApp', + id: 'discover', + order: 200, + path: 'path-to-discover', + + showOnHomePage: false, + solutionId: 'kibana', + subtitle: 'Search and find insights.', + title: 'Discover', + }, + { + category: FeatureCatalogueCategory.DATA, + description: 'Showcase your data in a pixel-perfect way.', + icon: 'canvasApp', + id: 'canvas', + order: 300, + path: 'path-to-canvas', + + showOnHomePage: false, + solutionId: 'kibana', + subtitle: 'Design pixel-perfect reports.', + title: 'Canvas', + }, + { + category: FeatureCatalogueCategory.DATA, + description: 'Explore geospatial data from Elasticsearch and the Elastic Maps Service.', + icon: 'gisApp', + id: 'maps', + order: 400, + path: 'path-to-maps', + showOnHomePage: false, + solutionId: 'kibana', + subtitle: 'Plot geographic data.', + title: 'Maps', + }, + { + category: FeatureCatalogueCategory.DATA, + description: + 'Automatically model the normal behavior of your time series data to detect anomalies.', + icon: 'machineLearningApp', + id: 'ml', + order: 500, + path: 'path-to-ml', + showOnHomePage: false, + solutionId: 'kibana', + subtitle: 'Model, predict, and detect.', + title: 'Machine Learning', + }, + { + category: FeatureCatalogueCategory.DATA, + description: 'Surface and analyze relevant relationships in your Elasticsearch data.', + icon: 'graphApp', + id: 'graph', + order: 600, + path: 'path-to-graph', + showOnHomePage: false, + solutionId: 'kibana', + subtitle: 'Reveal patterns and relationships.', + title: 'Graph', + }, +]; + +describe('GettingStarted', () => { + test('render', () => { + const component = shallowWithIntl( + + ); + expect(component).toMatchSnapshot(); + }); + test('dark mode on', () => { + const component = shallowWithIntl( + + ); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/kibana_overview/public/components/getting_started/getting_started.tsx b/src/plugins/kibana_overview/public/components/getting_started/getting_started.tsx new file mode 100644 index 0000000000000..9f2d714f43a53 --- /dev/null +++ b/src/plugins/kibana_overview/public/components/getting_started/getting_started.tsx @@ -0,0 +1,126 @@ +/* + * 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, { FC } from 'react'; +import { + EuiButton, + EuiCard, + EuiFlexGrid, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiImage, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { CoreStart } from 'kibana/public'; +import { RedirectAppLinks, useKibana } from '../../../../../../src/plugins/kibana_react/public'; +import { FeatureCatalogueEntry } from '../../../../../../src/plugins/home/public'; +import { PLUGIN_ID } from '../../../common'; + +interface Props { + addBasePath: (path: string) => string; + isDarkTheme: boolean; + apps: FeatureCatalogueEntry[]; +} + +export const GettingStarted: FC = ({ addBasePath, isDarkTheme, apps }) => { + const { + services: { application }, + } = useKibana(); + const gettingStartedGraphicURL = `/plugins/${PLUGIN_ID}/assets/kibana_montage_${ + isDarkTheme ? 'dark' : 'light' + }.svg`; + + return ( +
+ + +
+ +

+ +

+
+ + + + +

+ +

+
+ + + + + {apps.map(({ subtitle = '', icon, title }) => ( + + } + layout="horizontal" + paddingSize="none" + title={title} + titleElement="h3" + titleSize="xs" + /> + + ))} + + + + + + + + + +
+
+ + + + +
+
+ ); +}; diff --git a/src/plugins/kibana_overview/public/components/getting_started/index.ts b/src/plugins/kibana_overview/public/components/getting_started/index.ts new file mode 100644 index 0000000000000..6a0df12de2d2d --- /dev/null +++ b/src/plugins/kibana_overview/public/components/getting_started/index.ts @@ -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 './getting_started'; diff --git a/src/plugins/kibana_overview/public/components/manage_data/__snapshots__/manage_data.test.tsx.snap b/src/plugins/kibana_overview/public/components/manage_data/__snapshots__/manage_data.test.tsx.snap new file mode 100644 index 0000000000000..4be9e4df6b736 --- /dev/null +++ b/src/plugins/kibana_overview/public/components/manage_data/__snapshots__/manage_data.test.tsx.snap @@ -0,0 +1,103 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ManageData render 1`] = ` + + +`; + +exports[`ManageData render empty without any features 1`] = ``; diff --git a/src/plugins/kibana_overview/public/components/manage_data/index.tsx b/src/plugins/kibana_overview/public/components/manage_data/index.tsx new file mode 100644 index 0000000000000..2845e3bd12023 --- /dev/null +++ b/src/plugins/kibana_overview/public/components/manage_data/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 './manage_data'; diff --git a/src/plugins/kibana_overview/public/components/manage_data/manage_data.test.tsx b/src/plugins/kibana_overview/public/components/manage_data/manage_data.test.tsx new file mode 100644 index 0000000000000..3ce2364c96083 --- /dev/null +++ b/src/plugins/kibana_overview/public/components/manage_data/manage_data.test.tsx @@ -0,0 +1,83 @@ +/* + * 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 { ManageData } from './manage_data'; +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import { FeatureCatalogueCategory } from 'src/plugins/home/public'; + +const mockFeatures = [ + { + category: FeatureCatalogueCategory.ADMIN, + description: 'Control who has access and what tasks they can perform.', + icon: 'securityApp', + id: 'security', + order: 600, + path: 'path-to-security-roles', + title: 'Protect your data', + showOnHomePage: true, + }, + { + category: FeatureCatalogueCategory.ADMIN, + description: 'Track the real-time health and performance of your deployment.', + icon: 'monitoringApp', + id: 'monitoring', + order: 610, + path: 'path-to-monitoring', + title: 'Monitor the stack', + showOnHomePage: true, + }, + { + category: FeatureCatalogueCategory.ADMIN, + description: + 'Save snapshots to a backup repository, and restore to recover index and cluster state.', + icon: 'storage', + id: 'snapshot_restore', + order: 630, + path: 'path-to-snapshot-restore', + title: 'Store & recover backups', + showOnHomePage: true, + }, + { + category: FeatureCatalogueCategory.ADMIN, + description: 'Define lifecycle policies to automatically perform operations as an index ages.', + icon: 'indexSettings', + id: 'index_lifecycle_management', + order: 640, + path: 'path-to-index-lifecycle-management', + title: 'Manage index lifecycles', + showOnHomePage: true, + }, +]; + +const addBasePathMock = jest.fn((path: string) => (path ? path : 'path')); + +describe('ManageData', () => { + test('render', () => { + const component = shallowWithIntl( + + ); + expect(component).toMatchSnapshot(); + }); + + test('render empty without any features', () => { + const component = shallowWithIntl(); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/kibana_overview/public/components/manage_data/manage_data.tsx b/src/plugins/kibana_overview/public/components/manage_data/manage_data.tsx new file mode 100644 index 0000000000000..f7a40b9370efd --- /dev/null +++ b/src/plugins/kibana_overview/public/components/manage_data/manage_data.tsx @@ -0,0 +1,95 @@ +/* + * 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, { FC } from 'react'; +import PropTypes from 'prop-types'; +import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiSpacer, EuiTitle } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { CoreStart } from 'kibana/public'; +import { RedirectAppLinks, useKibana } from '../../../../../../src/plugins/kibana_react/public'; +import { FeatureCatalogueEntry } from '../../../../../../src/plugins/home/public'; +// @ts-expect-error untyped component +import { Synopsis } from '../synopsis'; + +interface Props { + addBasePath: (path: string) => string; + features: FeatureCatalogueEntry[]; +} + +export const ManageData: FC = ({ addBasePath, features }) => { + const { + services: { application }, + } = useKibana(); + return ( + <> + {features.length > 1 ?