[Home] Adding file upload to add data page (#100863)
* [Home] Adding file upload to add data page * updating comment * tiny refactor * attempting to reduce bundle size * reverting to original home register import * lazy load tab contents * changes based on review Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
f4f8ea73f9
commit
ad09cdc509
|
@ -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: <SampleDataSetCards addBasePath={this.props.addBasePath} />,
|
||||
},
|
||||
...extraTabs.map(({ id, name, component: Component }) => ({
|
||||
id,
|
||||
name,
|
||||
content: <Component />,
|
||||
})),
|
||||
];
|
||||
|
||||
let openTab = ALL_TAB_ID;
|
||||
|
@ -190,8 +198,9 @@ class TutorialDirectoryUi extends React.Component {
|
|||
};
|
||||
|
||||
renderTabContent = () => {
|
||||
if (this.state.selectedTabId === SAMPLE_DATA_TAB_ID) {
|
||||
return <SampleDataSetCards addBasePath={this.props.addBasePath} />;
|
||||
const tab = this.tabs.find(({ id }) => id === this.state.selectedTabId);
|
||||
if (tab?.content) {
|
||||
return tab.content;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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({}),
|
||||
});
|
||||
|
||||
|
|
|
@ -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),
|
||||
}));
|
||||
|
|
|
@ -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<ConfigSchema>) {}
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -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<AddDataServiceSetup> => {
|
||||
const setup = {
|
||||
registerAddDataTab: jest.fn(),
|
||||
};
|
||||
return setup;
|
||||
};
|
||||
|
||||
const createMock = (): jest.Mocked<PublicMethodsOf<AddDataService>> => {
|
||||
const service = {
|
||||
setup: jest.fn(),
|
||||
getAddDataTabs: jest.fn(() => []),
|
||||
};
|
||||
service.setup.mockImplementation(createSetupMock);
|
||||
return service;
|
||||
};
|
||||
|
||||
export const addDataServiceMock = {
|
||||
createSetup: createSetupMock,
|
||||
create: createMock,
|
||||
};
|
|
@ -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: () => <a>123</a> });
|
||||
setup.registerAddDataTab({ id: 'def', name: 'a b c', component: () => <a>456</a> });
|
||||
}).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: () => <a>123</a> });
|
||||
setup.registerAddDataTab({ id: 'abc', name: 'a b c', component: () => <a>456</a> });
|
||||
}).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: () => <a>123</a> },
|
||||
{ id: 'def', name: 'a b c', component: () => <a>456</a> },
|
||||
];
|
||||
setup.registerAddDataTab(links[0]);
|
||||
setup.registerAddDataTab(links[1]);
|
||||
expect(service.getAddDataTabs()).toEqual(links);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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<string, AddDataTab> = {};
|
||||
|
||||
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<AddDataService['setup']>;
|
11
src/plugins/home/public/services/add_data/index.ts
Normal file
11
src/plugins/home/public/services/add_data/index.ts
Normal file
|
@ -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';
|
|
@ -26,3 +26,6 @@ export type {
|
|||
TutorialDirectoryHeaderLinkComponent,
|
||||
TutorialModuleNoticeComponent,
|
||||
} from './tutorials';
|
||||
|
||||
export { AddDataService } from './add_data';
|
||||
export type { AddDataServiceSetup, AddDataTab } from './add_data';
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
],
|
||||
"optionalPlugins": [
|
||||
"security",
|
||||
"maps"
|
||||
"maps",
|
||||
"home"
|
||||
],
|
||||
"requiredBundles": [
|
||||
"kibanaReact",
|
||||
|
|
|
@ -28,3 +28,7 @@ export const FileDataVisualizer: FC = () => {
|
|||
</KibanaContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
// exporting as default so it can be used with React.lazy
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default FileDataVisualizer;
|
||||
|
|
|
@ -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 (
|
||||
<React.Suspense fallback={<div />}>
|
||||
<FileDataVisualizerComponent />
|
||||
</React.Suspense>
|
||||
);
|
||||
};
|
|
@ -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);
|
||||
|
|
20
x-pack/plugins/file_data_visualizer/public/register_home.ts
Normal file
20
x-pack/plugins/file_data_visualizer/public/register_home.ts
Normal file
|
@ -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,
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue