diff --git a/src/plugins/home/public/application/components/tutorial_directory.js b/src/plugins/home/public/application/components/tutorial_directory.js
index 094ac302fcebe..1fda865ebd847 100644
--- a/src/plugins/home/public/application/components/tutorial_directory.js
+++ b/src/plugins/home/public/application/components/tutorial_directory.js
@@ -42,6 +42,8 @@ class TutorialDirectoryUi extends React.Component {
constructor(props) {
super(props);
+ const extraTabs = getServices().addDataService.getAddDataTabs();
+
this.tabs = [
{
id: ALL_TAB_ID,
@@ -77,7 +79,13 @@ class TutorialDirectoryUi extends React.Component {
id: 'home.tutorial.tabs.sampleDataTitle',
defaultMessage: 'Sample data',
}),
+ content: ,
},
+ ...extraTabs.map(({ id, name, component: Component }) => ({
+ id,
+ name,
+ content: ,
+ })),
];
let openTab = ALL_TAB_ID;
@@ -190,8 +198,9 @@ class TutorialDirectoryUi extends React.Component {
};
renderTabContent = () => {
- if (this.state.selectedTabId === SAMPLE_DATA_TAB_ID) {
- return ;
+ const tab = this.tabs.find(({ id }) => id === this.state.selectedTabId);
+ if (tab?.content) {
+ return tab.content;
}
return (
diff --git a/src/plugins/home/public/application/kibana_services.ts b/src/plugins/home/public/application/kibana_services.ts
index 73a8ab41bcfd2..af9f956889547 100644
--- a/src/plugins/home/public/application/kibana_services.ts
+++ b/src/plugins/home/public/application/kibana_services.ts
@@ -20,6 +20,7 @@ import { UiCounterMetricType } from '@kbn/analytics';
import { TelemetryPluginStart } from '../../../telemetry/public';
import { UrlForwardingStart } from '../../../url_forwarding/public';
import { TutorialService } from '../services/tutorials';
+import { AddDataService } from '../services/add_data';
import { FeatureCatalogueRegistry } from '../services/feature_catalogue';
import { EnvironmentService } from '../services/environment';
import { ConfigSchema } from '../../config';
@@ -44,6 +45,7 @@ export interface HomeKibanaServices {
environmentService: EnvironmentService;
telemetry?: TelemetryPluginStart;
tutorialService: TutorialService;
+ addDataService: AddDataService;
}
let services: HomeKibanaServices | null = null;
diff --git a/src/plugins/home/public/mocks.ts b/src/plugins/home/public/mocks.ts
index 32bec31153ba0..10c186ee3f4e3 100644
--- a/src/plugins/home/public/mocks.ts
+++ b/src/plugins/home/public/mocks.ts
@@ -10,11 +10,13 @@ import { featureCatalogueRegistryMock } from './services/feature_catalogue/featu
import { environmentServiceMock } from './services/environment/environment.mock';
import { configSchema } from '../config';
import { tutorialServiceMock } from './services/tutorials/tutorial_service.mock';
+import { addDataServiceMock } from './services/add_data/add_data_service.mock';
const createSetupContract = () => ({
featureCatalogue: featureCatalogueRegistryMock.createSetup(),
environment: environmentServiceMock.createSetup(),
tutorials: tutorialServiceMock.createSetup(),
+ addData: addDataServiceMock.createSetup(),
config: configSchema.validate({}),
});
diff --git a/src/plugins/home/public/plugin.test.mocks.ts b/src/plugins/home/public/plugin.test.mocks.ts
index 779ab2e700352..c3e3c50a2fe0f 100644
--- a/src/plugins/home/public/plugin.test.mocks.ts
+++ b/src/plugins/home/public/plugin.test.mocks.ts
@@ -9,12 +9,15 @@
import { featureCatalogueRegistryMock } from './services/feature_catalogue/feature_catalogue_registry.mock';
import { environmentServiceMock } from './services/environment/environment.mock';
import { tutorialServiceMock } from './services/tutorials/tutorial_service.mock';
+import { addDataServiceMock } from './services/add_data/add_data_service.mock';
export const registryMock = featureCatalogueRegistryMock.create();
export const environmentMock = environmentServiceMock.create();
export const tutorialMock = tutorialServiceMock.create();
+export const addDataMock = addDataServiceMock.create();
jest.doMock('./services', () => ({
FeatureCatalogueRegistry: jest.fn(() => registryMock),
EnvironmentService: jest.fn(() => environmentMock),
TutorialService: jest.fn(() => tutorialMock),
+ AddDataService: jest.fn(() => addDataMock),
}));
diff --git a/src/plugins/home/public/plugin.ts b/src/plugins/home/public/plugin.ts
index 89c7600a1d85d..b3b5ce487b747 100644
--- a/src/plugins/home/public/plugin.ts
+++ b/src/plugins/home/public/plugin.ts
@@ -24,6 +24,8 @@ import {
FeatureCatalogueRegistrySetup,
TutorialService,
TutorialServiceSetup,
+ AddDataService,
+ AddDataServiceSetup,
} from './services';
import { ConfigSchema } from '../config';
import { setServices } from './application/kibana_services';
@@ -56,6 +58,7 @@ export class HomePublicPlugin
private readonly featuresCatalogueRegistry = new FeatureCatalogueRegistry();
private readonly environmentService = new EnvironmentService();
private readonly tutorialService = new TutorialService();
+ private readonly addDataService = new AddDataService();
constructor(private readonly initializerContext: PluginInitializerContext) {}
@@ -94,6 +97,7 @@ export class HomePublicPlugin
urlForwarding: urlForwardingStart,
homeConfig: this.initializerContext.config.get(),
tutorialService: this.tutorialService,
+ addDataService: this.addDataService,
featureCatalogue: this.featuresCatalogueRegistry,
});
coreStart.chrome.docTitle.change(
@@ -126,6 +130,7 @@ export class HomePublicPlugin
featureCatalogue,
environment: { ...this.environmentService.setup() },
tutorials: { ...this.tutorialService.setup() },
+ addData: { ...this.addDataService.setup() },
};
}
@@ -163,9 +168,13 @@ export type EnvironmentSetup = EnvironmentServiceSetup;
/** @public */
export type TutorialSetup = TutorialServiceSetup;
+/** @public */
+export type AddDataSetup = AddDataServiceSetup;
+
/** @public */
export interface HomePublicPluginSetup {
tutorials: TutorialServiceSetup;
+ addData: AddDataServiceSetup;
featureCatalogue: FeatureCatalogueSetup;
/**
* The environment service is only available for a transition period and will
diff --git a/src/plugins/home/public/services/add_data/add_data_service.mock.ts b/src/plugins/home/public/services/add_data/add_data_service.mock.ts
new file mode 100644
index 0000000000000..e0b4d12909791
--- /dev/null
+++ b/src/plugins/home/public/services/add_data/add_data_service.mock.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import type { PublicMethodsOf } from '@kbn/utility-types';
+import { AddDataService, AddDataServiceSetup } from './add_data_service';
+
+const createSetupMock = (): jest.Mocked => {
+ const setup = {
+ registerAddDataTab: jest.fn(),
+ };
+ return setup;
+};
+
+const createMock = (): jest.Mocked> => {
+ const service = {
+ setup: jest.fn(),
+ getAddDataTabs: jest.fn(() => []),
+ };
+ service.setup.mockImplementation(createSetupMock);
+ return service;
+};
+
+export const addDataServiceMock = {
+ createSetup: createSetupMock,
+ create: createMock,
+};
diff --git a/src/plugins/home/public/services/add_data/add_data_service.test.tsx b/src/plugins/home/public/services/add_data/add_data_service.test.tsx
new file mode 100644
index 0000000000000..b04b80ac19eec
--- /dev/null
+++ b/src/plugins/home/public/services/add_data/add_data_service.test.tsx
@@ -0,0 +1,49 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React from 'react';
+import { AddDataService } from './add_data_service';
+
+describe('AddDataService', () => {
+ describe('setup', () => {
+ test('allows multiple register directory header link calls', () => {
+ const setup = new AddDataService().setup();
+ expect(() => {
+ setup.registerAddDataTab({ id: 'abc', name: 'a b c', component: () => 123 });
+ setup.registerAddDataTab({ id: 'def', name: 'a b c', component: () => 456 });
+ }).not.toThrow();
+ });
+
+ test('throws when same directory header link is registered twice', () => {
+ const setup = new AddDataService().setup();
+ expect(() => {
+ setup.registerAddDataTab({ id: 'abc', name: 'a b c', component: () => 123 });
+ setup.registerAddDataTab({ id: 'abc', name: 'a b c', component: () => 456 });
+ }).toThrow();
+ });
+ });
+
+ describe('getDirectoryHeaderLinks', () => {
+ test('returns empty array', () => {
+ const service = new AddDataService();
+ expect(service.getAddDataTabs()).toEqual([]);
+ });
+
+ test('returns last state of register calls', () => {
+ const service = new AddDataService();
+ const setup = service.setup();
+ const links = [
+ { id: 'abc', name: 'a b c', component: () => 123 },
+ { id: 'def', name: 'a b c', component: () => 456 },
+ ];
+ setup.registerAddDataTab(links[0]);
+ setup.registerAddDataTab(links[1]);
+ expect(service.getAddDataTabs()).toEqual(links);
+ });
+ });
+});
diff --git a/src/plugins/home/public/services/add_data/add_data_service.ts b/src/plugins/home/public/services/add_data/add_data_service.ts
new file mode 100644
index 0000000000000..668c373f8314d
--- /dev/null
+++ b/src/plugins/home/public/services/add_data/add_data_service.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React from 'react';
+
+/** @public */
+export interface AddDataTab {
+ id: string;
+ name: string;
+ component: React.FC;
+}
+
+export class AddDataService {
+ private addDataTabs: Record = {};
+
+ public setup() {
+ return {
+ /**
+ * Registers a component that will be rendered as a new tab in the Add data page
+ */
+ registerAddDataTab: (tab: AddDataTab) => {
+ if (this.addDataTabs[tab.id]) {
+ throw new Error(`Tab ${tab.id} already exists`);
+ }
+ this.addDataTabs[tab.id] = tab;
+ },
+ };
+ }
+
+ public getAddDataTabs() {
+ return Object.values(this.addDataTabs);
+ }
+}
+
+export type AddDataServiceSetup = ReturnType;
diff --git a/src/plugins/home/public/services/add_data/index.ts b/src/plugins/home/public/services/add_data/index.ts
new file mode 100644
index 0000000000000..f2367ca320e9f
--- /dev/null
+++ b/src/plugins/home/public/services/add_data/index.ts
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+export { AddDataService } from './add_data_service';
+
+export type { AddDataServiceSetup, AddDataTab } from './add_data_service';
diff --git a/src/plugins/home/public/services/index.ts b/src/plugins/home/public/services/index.ts
index 8cd4c8d84e0f7..65913df6310b1 100644
--- a/src/plugins/home/public/services/index.ts
+++ b/src/plugins/home/public/services/index.ts
@@ -26,3 +26,6 @@ export type {
TutorialDirectoryHeaderLinkComponent,
TutorialModuleNoticeComponent,
} from './tutorials';
+
+export { AddDataService } from './add_data';
+export type { AddDataServiceSetup, AddDataTab } from './add_data';
diff --git a/x-pack/plugins/file_data_visualizer/kibana.json b/x-pack/plugins/file_data_visualizer/kibana.json
index 721352cff7c95..eea52bb6e98b2 100644
--- a/x-pack/plugins/file_data_visualizer/kibana.json
+++ b/x-pack/plugins/file_data_visualizer/kibana.json
@@ -14,7 +14,8 @@
],
"optionalPlugins": [
"security",
- "maps"
+ "maps",
+ "home"
],
"requiredBundles": [
"kibanaReact",
diff --git a/x-pack/plugins/file_data_visualizer/public/application/file_datavisualizer.tsx b/x-pack/plugins/file_data_visualizer/public/application/file_datavisualizer.tsx
index f291076557bb8..c8f327496842a 100644
--- a/x-pack/plugins/file_data_visualizer/public/application/file_datavisualizer.tsx
+++ b/x-pack/plugins/file_data_visualizer/public/application/file_datavisualizer.tsx
@@ -28,3 +28,7 @@ export const FileDataVisualizer: FC = () => {
);
};
+
+// exporting as default so it can be used with React.lazy
+// eslint-disable-next-line import/no-default-export
+export default FileDataVisualizer;
diff --git a/x-pack/plugins/file_data_visualizer/public/lazy_load_bundle/component_wrapper.tsx b/x-pack/plugins/file_data_visualizer/public/lazy_load_bundle/component_wrapper.tsx
new file mode 100644
index 0000000000000..e6835d9e7a668
--- /dev/null
+++ b/x-pack/plugins/file_data_visualizer/public/lazy_load_bundle/component_wrapper.tsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { FC } from 'react';
+
+const FileDataVisualizerComponent = React.lazy(() => import('../application/file_datavisualizer'));
+
+export const FileDataVisualizerWrapper: FC = () => {
+ return (
+ }>
+
+
+ );
+};
diff --git a/x-pack/plugins/file_data_visualizer/public/plugin.ts b/x-pack/plugins/file_data_visualizer/public/plugin.ts
index a94c0fce45cd4..0064f96195eaf 100644
--- a/x-pack/plugins/file_data_visualizer/public/plugin.ts
+++ b/x-pack/plugins/file_data_visualizer/public/plugin.ts
@@ -5,21 +5,24 @@
* 2.0.
*/
-import { CoreStart } from 'kibana/public';
+import { CoreSetup, CoreStart } from 'kibana/public';
import type { EmbeddableStart } from '../../../../src/plugins/embeddable/public';
import type { SharePluginStart } from '../../../../src/plugins/share/public';
import { Plugin } from '../../../../src/core/public';
import { setStartServices } from './kibana_services';
-import { DataPublicPluginStart } from '../../../../src/plugins/data/public';
+import type { DataPublicPluginStart } from '../../../../src/plugins/data/public';
+import type { HomePublicPluginSetup } from '../../../../src/plugins/home/public';
import type { FileUploadPluginStart } from '../../file_upload/public';
import type { MapsStartApi } from '../../maps/public';
import type { SecurityPluginSetup } from '../../security/public';
import { getFileDataVisualizerComponent } from './api';
import { getMaxBytesFormatted } from './application/util/get_max_bytes';
+import { registerHomeAddData } from './register_home';
-// eslint-disable-next-line @typescript-eslint/no-empty-interface
-export interface FileDataVisualizerSetupDependencies {}
+export interface FileDataVisualizerSetupDependencies {
+ home?: HomePublicPluginSetup;
+}
export interface FileDataVisualizerStartDependencies {
data: DataPublicPluginStart;
fileUpload: FileUploadPluginStart;
@@ -40,7 +43,11 @@ export class FileDataVisualizerPlugin
FileDataVisualizerSetupDependencies,
FileDataVisualizerStartDependencies
> {
- public setup() {}
+ public setup(core: CoreSetup, plugins: FileDataVisualizerSetupDependencies) {
+ if (plugins.home) {
+ registerHomeAddData(plugins.home);
+ }
+ }
public start(core: CoreStart, plugins: FileDataVisualizerStartDependencies) {
setStartServices(core, plugins);
diff --git a/x-pack/plugins/file_data_visualizer/public/register_home.ts b/x-pack/plugins/file_data_visualizer/public/register_home.ts
new file mode 100644
index 0000000000000..e54c37a8d06bc
--- /dev/null
+++ b/x-pack/plugins/file_data_visualizer/public/register_home.ts
@@ -0,0 +1,20 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { i18n } from '@kbn/i18n';
+import type { HomePublicPluginSetup } from '../../../../src/plugins/home/public';
+import { FileDataVisualizerWrapper } from './lazy_load_bundle/component_wrapper';
+
+export function registerHomeAddData(home: HomePublicPluginSetup) {
+ home.addData.registerAddDataTab({
+ id: 'fileDataViz',
+ name: i18n.translate('xpack.fileDataVisualizer.embeddedTabTitle', {
+ defaultMessage: 'Upload file',
+ }),
+ component: FileDataVisualizerWrapper,
+ });
+}