mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
Switch to embeddable factory interface with optional override (#61165)
* wip * typescript map embeddable * More updates * Address code review comments and update some usages in SIEM and uptime to the new types * More clean up - carry over some of the SIEM types to maps for render tool tip * fixes * fixes * Address more review comments * fixes * fixes * fix jest test * Fix visualize embeddable * fixes after master merge * Fixes * Prefix variable with name "custom" to make it more obvious * Remove layerList from input state * fixes * Update src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx Co-Authored-By: Vadim Dalecky <streamich@users.noreply.github.com> * review updates * fixes * update maps readme Co-authored-by: Vadim Dalecky <streamich@users.noreply.github.com>
This commit is contained in:
parent
09f1bae2ee
commit
bb747abdaf
66 changed files with 867 additions and 655 deletions
|
@ -21,11 +21,11 @@ import { i18n } from '@kbn/i18n';
|
|||
import {
|
||||
IContainer,
|
||||
EmbeddableInput,
|
||||
EmbeddableFactory,
|
||||
EmbeddableFactoryDefinition,
|
||||
} from '../../../../src/plugins/embeddable/public';
|
||||
import { HelloWorldEmbeddable, HELLO_WORLD_EMBEDDABLE } from './hello_world_embeddable';
|
||||
|
||||
export class HelloWorldEmbeddableFactory extends EmbeddableFactory {
|
||||
export class HelloWorldEmbeddableFactory implements EmbeddableFactoryDefinition {
|
||||
public readonly type = HELLO_WORLD_EMBEDDABLE;
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EmbeddableFactory,
|
||||
EmbeddableFactoryDefinition,
|
||||
ContainerInput,
|
||||
EmbeddableStart,
|
||||
} from '../../../../src/plugins/embeddable/public';
|
||||
|
@ -29,22 +29,20 @@ interface StartServices {
|
|||
getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory'];
|
||||
}
|
||||
|
||||
export class ListContainerFactory extends EmbeddableFactory {
|
||||
export class ListContainerFactory implements EmbeddableFactoryDefinition {
|
||||
public readonly type = LIST_CONTAINER;
|
||||
public readonly isContainerType = true;
|
||||
|
||||
constructor(private getStartServices: () => Promise<StartServices>) {
|
||||
super();
|
||||
}
|
||||
constructor(private getStartServices: () => Promise<StartServices>) {}
|
||||
|
||||
public async isEditable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public async create(initialInput: ContainerInput) {
|
||||
public create = async (initialInput: ContainerInput) => {
|
||||
const { getEmbeddableFactory } = await this.getStartServices();
|
||||
return new ListContainer(initialInput, getEmbeddableFactory);
|
||||
}
|
||||
};
|
||||
|
||||
public getDisplayName() {
|
||||
return i18n.translate('embeddableExamples.searchableListContainer.displayName', {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { IContainer, EmbeddableFactory } from '../../../../src/plugins/embeddable/public';
|
||||
import { IContainer, EmbeddableFactoryDefinition } from '../../../../src/plugins/embeddable/public';
|
||||
import {
|
||||
MultiTaskTodoEmbeddable,
|
||||
MULTI_TASK_TODO_EMBEDDABLE,
|
||||
|
@ -26,10 +26,8 @@ import {
|
|||
MultiTaskTodoOutput,
|
||||
} from './multi_task_todo_embeddable';
|
||||
|
||||
export class MultiTaskTodoEmbeddableFactory extends EmbeddableFactory<
|
||||
MultiTaskTodoInput,
|
||||
MultiTaskTodoOutput
|
||||
> {
|
||||
export class MultiTaskTodoEmbeddableFactory
|
||||
implements EmbeddableFactoryDefinition<MultiTaskTodoInput, MultiTaskTodoOutput> {
|
||||
public readonly type = MULTI_TASK_TODO_EMBEDDABLE;
|
||||
|
||||
public async isEditable() {
|
||||
|
|
|
@ -18,7 +18,10 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EmbeddableFactory, EmbeddableStart } from '../../../../src/plugins/embeddable/public';
|
||||
import {
|
||||
EmbeddableFactoryDefinition,
|
||||
EmbeddableStart,
|
||||
} from '../../../../src/plugins/embeddable/public';
|
||||
import {
|
||||
SEARCHABLE_LIST_CONTAINER,
|
||||
SearchableListContainer,
|
||||
|
@ -29,22 +32,20 @@ interface StartServices {
|
|||
getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory'];
|
||||
}
|
||||
|
||||
export class SearchableListContainerFactory extends EmbeddableFactory {
|
||||
export class SearchableListContainerFactory implements EmbeddableFactoryDefinition {
|
||||
public readonly type = SEARCHABLE_LIST_CONTAINER;
|
||||
public readonly isContainerType = true;
|
||||
|
||||
constructor(private getStartServices: () => Promise<StartServices>) {
|
||||
super();
|
||||
}
|
||||
constructor(private getStartServices: () => Promise<StartServices>) {}
|
||||
|
||||
public async isEditable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public async create(initialInput: SearchableContainerInput) {
|
||||
public create = async (initialInput: SearchableContainerInput) => {
|
||||
const { getEmbeddableFactory } = await this.getStartServices();
|
||||
return new SearchableListContainer(initialInput, getEmbeddableFactory);
|
||||
}
|
||||
};
|
||||
|
||||
public getDisplayName() {
|
||||
return i18n.translate('embeddableExamples.searchableListContainer.displayName', {
|
||||
|
|
|
@ -23,7 +23,7 @@ import { OverlayStart } from 'kibana/public';
|
|||
import { EuiFieldText } from '@elastic/eui';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import { toMountPoint } from '../../../../src/plugins/kibana_react/public';
|
||||
import { IContainer, EmbeddableFactory } from '../../../../src/plugins/embeddable/public';
|
||||
import { IContainer, EmbeddableFactoryDefinition } from '../../../../src/plugins/embeddable/public';
|
||||
import { TodoEmbeddable, TODO_EMBEDDABLE, TodoInput, TodoOutput } from './todo_embeddable';
|
||||
|
||||
function TaskInput({ onSave }: { onSave: (task: string) => void }) {
|
||||
|
@ -47,16 +47,11 @@ interface StartServices {
|
|||
openModal: OverlayStart['openModal'];
|
||||
}
|
||||
|
||||
export class TodoEmbeddableFactory extends EmbeddableFactory<
|
||||
TodoInput,
|
||||
TodoOutput,
|
||||
TodoEmbeddable
|
||||
> {
|
||||
export class TodoEmbeddableFactory
|
||||
implements EmbeddableFactoryDefinition<TodoInput, TodoOutput, TodoEmbeddable> {
|
||||
public readonly type = TODO_EMBEDDABLE;
|
||||
|
||||
constructor(private getStartServices: () => Promise<StartServices>) {
|
||||
super();
|
||||
}
|
||||
constructor(private getStartServices: () => Promise<StartServices>) {}
|
||||
|
||||
public async isEditable() {
|
||||
return true;
|
||||
|
@ -72,7 +67,7 @@ export class TodoEmbeddableFactory extends EmbeddableFactory<
|
|||
* used to collect specific embeddable input that the container will not provide, like
|
||||
* in this case, the task string.
|
||||
*/
|
||||
public async getExplicitInput() {
|
||||
public getExplicitInput = async () => {
|
||||
const { openModal } = await this.getStartServices();
|
||||
return new Promise<{ task: string }>(resolve => {
|
||||
const onSave = (task: string) => resolve({ task });
|
||||
|
@ -87,7 +82,7 @@ export class TodoEmbeddableFactory extends EmbeddableFactory<
|
|||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
public getDisplayName() {
|
||||
return i18n.translate('embeddableExamples.todo.displayName', {
|
||||
|
|
|
@ -37,9 +37,14 @@ import {
|
|||
import {
|
||||
TodoEmbeddable,
|
||||
TODO_EMBEDDABLE,
|
||||
TodoEmbeddableFactory,
|
||||
TodoInput,
|
||||
} from '../../../examples/embeddable_examples/public/todo';
|
||||
import { EmbeddableStart, EmbeddableRoot } from '../../../src/plugins/embeddable/public';
|
||||
import {
|
||||
EmbeddableStart,
|
||||
EmbeddableRoot,
|
||||
EmbeddableOutput,
|
||||
ErrorEmbeddable,
|
||||
} from '../../../src/plugins/embeddable/public';
|
||||
|
||||
interface Props {
|
||||
getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory'];
|
||||
|
@ -53,7 +58,7 @@ interface State {
|
|||
}
|
||||
|
||||
export class TodoEmbeddableExample extends React.Component<Props, State> {
|
||||
private embeddable?: TodoEmbeddable;
|
||||
private embeddable?: TodoEmbeddable | ErrorEmbeddable;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
@ -62,7 +67,9 @@ export class TodoEmbeddableExample extends React.Component<Props, State> {
|
|||
}
|
||||
|
||||
public componentDidMount() {
|
||||
const factory = this.props.getEmbeddableFactory(TODO_EMBEDDABLE) as TodoEmbeddableFactory;
|
||||
const factory = this.props.getEmbeddableFactory<TodoInput, EmbeddableOutput, TodoEmbeddable>(
|
||||
TODO_EMBEDDABLE
|
||||
);
|
||||
|
||||
if (factory === undefined) {
|
||||
throw new Error('Embeddable factory is undefined!');
|
||||
|
|
|
@ -48,7 +48,6 @@ import {
|
|||
import {
|
||||
DASHBOARD_CONTAINER_TYPE,
|
||||
DashboardContainer,
|
||||
DashboardContainerFactory,
|
||||
DashboardContainerInput,
|
||||
DashboardPanelState,
|
||||
} from '../../../../../../plugins/dashboard/public';
|
||||
|
@ -58,6 +57,7 @@ import {
|
|||
isErrorEmbeddable,
|
||||
openAddPanelFlyout,
|
||||
ViewMode,
|
||||
ContainerOutput,
|
||||
} from '../../../../../../plugins/embeddable/public';
|
||||
import { NavAction, SavedDashboardPanel } from './types';
|
||||
|
||||
|
@ -307,83 +307,92 @@ export class DashboardAppController {
|
|||
let outputSubscription: Subscription | undefined;
|
||||
|
||||
const dashboardDom = document.getElementById('dashboardViewport');
|
||||
const dashboardFactory = embeddable.getEmbeddableFactory(
|
||||
DASHBOARD_CONTAINER_TYPE
|
||||
) as DashboardContainerFactory;
|
||||
dashboardFactory
|
||||
.create(getDashboardInput())
|
||||
.then((container: DashboardContainer | ErrorEmbeddable) => {
|
||||
if (!isErrorEmbeddable(container)) {
|
||||
dashboardContainer = container;
|
||||
const dashboardFactory = embeddable.getEmbeddableFactory<
|
||||
DashboardContainerInput,
|
||||
ContainerOutput,
|
||||
DashboardContainer
|
||||
>(DASHBOARD_CONTAINER_TYPE);
|
||||
|
||||
dashboardContainer.renderEmpty = () => {
|
||||
const shouldShowEditHelp = getShouldShowEditHelp();
|
||||
const shouldShowViewHelp = getShouldShowViewHelp();
|
||||
const isEmptyInReadOnlyMode = shouldShowUnauthorizedEmptyState();
|
||||
const isEmptyState = shouldShowEditHelp || shouldShowViewHelp || isEmptyInReadOnlyMode;
|
||||
return isEmptyState ? (
|
||||
<DashboardEmptyScreen
|
||||
{...getEmptyScreenProps(shouldShowEditHelp, isEmptyInReadOnlyMode)}
|
||||
/>
|
||||
) : null;
|
||||
};
|
||||
if (dashboardFactory) {
|
||||
dashboardFactory
|
||||
.create(getDashboardInput())
|
||||
.then((container: DashboardContainer | ErrorEmbeddable | undefined) => {
|
||||
if (container && !isErrorEmbeddable(container)) {
|
||||
dashboardContainer = container;
|
||||
|
||||
updateIndexPatterns(dashboardContainer);
|
||||
dashboardContainer.renderEmpty = () => {
|
||||
const shouldShowEditHelp = getShouldShowEditHelp();
|
||||
const shouldShowViewHelp = getShouldShowViewHelp();
|
||||
const isEmptyInReadOnlyMode = shouldShowUnauthorizedEmptyState();
|
||||
const isEmptyState =
|
||||
shouldShowEditHelp || shouldShowViewHelp || isEmptyInReadOnlyMode;
|
||||
return isEmptyState ? (
|
||||
<DashboardEmptyScreen
|
||||
{...getEmptyScreenProps(shouldShowEditHelp, isEmptyInReadOnlyMode)}
|
||||
/>
|
||||
) : null;
|
||||
};
|
||||
|
||||
outputSubscription = dashboardContainer.getOutput$().subscribe(() => {
|
||||
updateIndexPatterns(dashboardContainer);
|
||||
});
|
||||
|
||||
inputSubscription = dashboardContainer.getInput$().subscribe(() => {
|
||||
let dirty = false;
|
||||
|
||||
// This has to be first because handleDashboardContainerChanges causes
|
||||
// appState.save which will cause refreshDashboardContainer to be called.
|
||||
|
||||
if (
|
||||
!esFilters.compareFilters(
|
||||
container.getInput().filters,
|
||||
queryFilter.getFilters(),
|
||||
esFilters.COMPARE_ALL_OPTIONS
|
||||
)
|
||||
) {
|
||||
// Add filters modifies the object passed to it, hence the clone deep.
|
||||
queryFilter.addFilters(_.cloneDeep(container.getInput().filters));
|
||||
|
||||
dashboardStateManager.applyFilters($scope.model.query, container.getInput().filters);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
dashboardStateManager.handleDashboardContainerChanges(container);
|
||||
$scope.$evalAsync(() => {
|
||||
if (dirty) {
|
||||
updateState();
|
||||
}
|
||||
outputSubscription = dashboardContainer.getOutput$().subscribe(() => {
|
||||
updateIndexPatterns(dashboardContainer);
|
||||
});
|
||||
});
|
||||
|
||||
dashboardStateManager.registerChangeListener(() => {
|
||||
// we aren't checking dirty state because there are changes the container needs to know about
|
||||
// that won't make the dashboard "dirty" - like a view mode change.
|
||||
refreshDashboardContainer();
|
||||
});
|
||||
inputSubscription = dashboardContainer.getInput$().subscribe(() => {
|
||||
let dirty = false;
|
||||
|
||||
// This code needs to be replaced with a better mechanism for adding new embeddables of
|
||||
// any type from the add panel. Likely this will happen via creating a visualization "inline",
|
||||
// without navigating away from the UX.
|
||||
if ($routeParams[DashboardConstants.ADD_EMBEDDABLE_TYPE]) {
|
||||
const type = $routeParams[DashboardConstants.ADD_EMBEDDABLE_TYPE];
|
||||
const id = $routeParams[DashboardConstants.ADD_EMBEDDABLE_ID];
|
||||
container.addSavedObjectEmbeddable(type, id);
|
||||
removeQueryParam(history, DashboardConstants.ADD_EMBEDDABLE_TYPE);
|
||||
removeQueryParam(history, DashboardConstants.ADD_EMBEDDABLE_ID);
|
||||
// This has to be first because handleDashboardContainerChanges causes
|
||||
// appState.save which will cause refreshDashboardContainer to be called.
|
||||
|
||||
if (
|
||||
!esFilters.compareFilters(
|
||||
container.getInput().filters,
|
||||
queryFilter.getFilters(),
|
||||
esFilters.COMPARE_ALL_OPTIONS
|
||||
)
|
||||
) {
|
||||
// Add filters modifies the object passed to it, hence the clone deep.
|
||||
queryFilter.addFilters(_.cloneDeep(container.getInput().filters));
|
||||
|
||||
dashboardStateManager.applyFilters(
|
||||
$scope.model.query,
|
||||
container.getInput().filters
|
||||
);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
dashboardStateManager.handleDashboardContainerChanges(container);
|
||||
$scope.$evalAsync(() => {
|
||||
if (dirty) {
|
||||
updateState();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
dashboardStateManager.registerChangeListener(() => {
|
||||
// we aren't checking dirty state because there are changes the container needs to know about
|
||||
// that won't make the dashboard "dirty" - like a view mode change.
|
||||
refreshDashboardContainer();
|
||||
});
|
||||
|
||||
// This code needs to be replaced with a better mechanism for adding new embeddables of
|
||||
// any type from the add panel. Likely this will happen via creating a visualization "inline",
|
||||
// without navigating away from the UX.
|
||||
if ($routeParams[DashboardConstants.ADD_EMBEDDABLE_TYPE]) {
|
||||
const type = $routeParams[DashboardConstants.ADD_EMBEDDABLE_TYPE];
|
||||
const id = $routeParams[DashboardConstants.ADD_EMBEDDABLE_ID];
|
||||
container.addSavedObjectEmbeddable(type, id);
|
||||
removeQueryParam(history, DashboardConstants.ADD_EMBEDDABLE_TYPE);
|
||||
removeQueryParam(history, DashboardConstants.ADD_EMBEDDABLE_ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dashboardDom) {
|
||||
container.render(dashboardDom);
|
||||
}
|
||||
});
|
||||
if (dashboardDom && container) {
|
||||
container.render(dashboardDom);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Part of the exposed plugin API - do not remove without careful consideration.
|
||||
this.appStatus = {
|
||||
|
|
|
@ -22,9 +22,9 @@ import { i18n } from '@kbn/i18n';
|
|||
import { UiActionsStart } from 'src/plugins/ui_actions/public';
|
||||
import { getServices } from '../../kibana_services';
|
||||
import {
|
||||
EmbeddableFactory,
|
||||
ErrorEmbeddable,
|
||||
EmbeddableFactoryDefinition,
|
||||
Container,
|
||||
ErrorEmbeddable,
|
||||
} from '../../../../../../../plugins/embeddable/public';
|
||||
|
||||
import { TimeRange } from '../../../../../../../plugins/data/public';
|
||||
|
@ -37,28 +37,23 @@ interface StartServices {
|
|||
isEditable: () => boolean;
|
||||
}
|
||||
|
||||
export class SearchEmbeddableFactory extends EmbeddableFactory<
|
||||
SearchInput,
|
||||
SearchOutput,
|
||||
SearchEmbeddable
|
||||
> {
|
||||
export class SearchEmbeddableFactory
|
||||
implements EmbeddableFactoryDefinition<SearchInput, SearchOutput, SearchEmbeddable> {
|
||||
public readonly type = SEARCH_EMBEDDABLE_TYPE;
|
||||
private $injector: auto.IInjectorService | null;
|
||||
private getInjector: () => Promise<auto.IInjectorService> | null;
|
||||
public readonly savedObjectMetaData = {
|
||||
name: i18n.translate('kbn.discover.savedSearch.savedObjectName', {
|
||||
defaultMessage: 'Saved search',
|
||||
}),
|
||||
type: 'search',
|
||||
getIconForSavedObject: () => 'search',
|
||||
};
|
||||
|
||||
constructor(
|
||||
private getStartServices: () => Promise<StartServices>,
|
||||
getInjector: () => Promise<auto.IInjectorService>
|
||||
) {
|
||||
super({
|
||||
savedObjectMetaData: {
|
||||
name: i18n.translate('kbn.discover.savedSearch.savedObjectName', {
|
||||
defaultMessage: 'Saved search',
|
||||
}),
|
||||
type: 'search',
|
||||
getIconForSavedObject: () => 'search',
|
||||
},
|
||||
});
|
||||
this.$injector = null;
|
||||
this.getInjector = getInjector;
|
||||
}
|
||||
|
@ -67,9 +62,9 @@ export class SearchEmbeddableFactory extends EmbeddableFactory<
|
|||
return false;
|
||||
}
|
||||
|
||||
public async isEditable() {
|
||||
public isEditable = async () => {
|
||||
return (await this.getStartServices()).isEditable();
|
||||
}
|
||||
};
|
||||
|
||||
public getDisplayName() {
|
||||
return i18n.translate('kbn.embeddable.search.displayName', {
|
||||
|
@ -77,11 +72,11 @@ export class SearchEmbeddableFactory extends EmbeddableFactory<
|
|||
});
|
||||
}
|
||||
|
||||
public async createFromSavedObject(
|
||||
public createFromSavedObject = async (
|
||||
savedObjectId: string,
|
||||
input: Partial<SearchInput> & { id: string; timeRange: TimeRange },
|
||||
parent?: Container
|
||||
): Promise<SearchEmbeddable | ErrorEmbeddable> {
|
||||
): Promise<SearchEmbeddable | ErrorEmbeddable> => {
|
||||
if (!this.$injector) {
|
||||
this.$injector = await this.getInjector();
|
||||
}
|
||||
|
@ -115,7 +110,7 @@ export class SearchEmbeddableFactory extends EmbeddableFactory<
|
|||
console.error(e); // eslint-disable-line no-console
|
||||
return new ErrorEmbeddable(e, input, parent);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public async create(input: SearchInput) {
|
||||
return new ErrorEmbeddable('Saved searches can only be created from a saved object', input);
|
||||
|
|
|
@ -61,6 +61,7 @@ export interface VisualizeKibanaServices {
|
|||
I18nContext: I18nStart['Context'];
|
||||
setActiveUrl: (newUrl: string) => void;
|
||||
DefaultVisualizationEditor: typeof DefaultEditorController;
|
||||
createVisEmbeddableFromObject: VisualizationsStart['__LEGACY']['createVisEmbeddableFromObject'];
|
||||
}
|
||||
|
||||
let services: VisualizeKibanaServices | null = null;
|
||||
|
|
|
@ -43,7 +43,7 @@ import {
|
|||
import { createSavedSearchesLoader } from '../../../../../../plugins/discover/public';
|
||||
|
||||
const getResolvedResults = deps => {
|
||||
const { core, data, visualizations } = deps;
|
||||
const { core, data, visualizations, createVisEmbeddableFromObject } = deps;
|
||||
|
||||
const results = {};
|
||||
|
||||
|
@ -60,7 +60,7 @@ const getResolvedResults = deps => {
|
|||
})
|
||||
.then(vis => {
|
||||
results.vis = vis;
|
||||
return deps.embeddable.getEmbeddableFactory('visualization').createFromObject(results.vis, {
|
||||
return createVisEmbeddableFromObject(vis, {
|
||||
timeRange: data.query.timefilter.timefilter.getTime(),
|
||||
filters: data.query.filterManager.getFilters(),
|
||||
});
|
||||
|
|
|
@ -156,6 +156,7 @@ export class VisualizePlugin implements Plugin {
|
|||
I18nContext: coreStart.i18n.Context,
|
||||
setActiveUrl,
|
||||
DefaultVisualizationEditor: DefaultEditorController,
|
||||
createVisEmbeddableFromObject: visualizations.__LEGACY.createVisEmbeddableFromObject,
|
||||
};
|
||||
setServices(deps);
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { isErrorEmbeddable, EmbeddableFactory } from '../embeddable_plugin';
|
||||
import { isErrorEmbeddable } from '../embeddable_plugin';
|
||||
import { ExpandPanelAction } from './expand_panel_action';
|
||||
import { DashboardContainer } from '../embeddable';
|
||||
import { getSampleDashboardInput, getSampleDashboardPanel } from '../test_helpers';
|
||||
|
@ -29,11 +29,16 @@ import {
|
|||
ContactCardEmbeddableOutput,
|
||||
} from '../embeddable_plugin_test_samples';
|
||||
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(
|
||||
// eslint-disable-next-line
|
||||
import { embeddablePluginMock } from 'src/plugins/embeddable/public/mocks';
|
||||
|
||||
const { setup, doStart } = embeddablePluginMock.createInstance();
|
||||
|
||||
setup.registerEmbeddableFactory(
|
||||
CONTACT_CARD_EMBEDDABLE,
|
||||
new ContactCardEmbeddableFactory({} as any, (() => null) as any, {} as any)
|
||||
new ContactCardEmbeddableFactory((() => null) as any, {} as any)
|
||||
);
|
||||
const start = doStart();
|
||||
|
||||
let container: DashboardContainer;
|
||||
let embeddable: ContactCardEmbeddable;
|
||||
|
@ -43,9 +48,7 @@ beforeEach(async () => {
|
|||
ExitFullScreenButton: () => null,
|
||||
SavedObjectFinder: () => null,
|
||||
application: {} as any,
|
||||
embeddable: {
|
||||
getEmbeddableFactory: (id: string) => embeddableFactories.get(id)!,
|
||||
} as any,
|
||||
embeddable: start,
|
||||
inspector: {} as any,
|
||||
notifications: {} as any,
|
||||
overlays: {} as any,
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { isErrorEmbeddable, EmbeddableFactory } from '../embeddable_plugin';
|
||||
import { isErrorEmbeddable } from '../embeddable_plugin';
|
||||
import { ReplacePanelAction } from './replace_panel_action';
|
||||
import { DashboardContainer } from '../embeddable';
|
||||
import { getSampleDashboardInput, getSampleDashboardPanel } from '../test_helpers';
|
||||
|
@ -30,12 +30,15 @@ import {
|
|||
import { coreMock } from '../../../../core/public/mocks';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(
|
||||
// eslint-disable-next-line
|
||||
import { embeddablePluginMock } from 'src/plugins/embeddable/public/mocks';
|
||||
|
||||
const { setup, doStart } = embeddablePluginMock.createInstance();
|
||||
setup.registerEmbeddableFactory(
|
||||
CONTACT_CARD_EMBEDDABLE,
|
||||
new ContactCardEmbeddableFactory({} as any, (() => null) as any, {} as any)
|
||||
new ContactCardEmbeddableFactory((() => null) as any, {} as any)
|
||||
);
|
||||
const getEmbeddableFactories = () => embeddableFactories.values();
|
||||
const start = doStart();
|
||||
|
||||
let container: DashboardContainer;
|
||||
let embeddable: ContactCardEmbeddable;
|
||||
|
@ -46,9 +49,7 @@ beforeEach(async () => {
|
|||
ExitFullScreenButton: () => null,
|
||||
SavedObjectFinder: () => null,
|
||||
application: {} as any,
|
||||
embeddable: {
|
||||
getEmbeddableFactory: (id: string) => embeddableFactories.get(id)!,
|
||||
} as any,
|
||||
embeddable: start,
|
||||
inspector: {} as any,
|
||||
notifications: {} as any,
|
||||
overlays: coreStart.overlays,
|
||||
|
@ -87,7 +88,7 @@ test('Executes the replace panel action', async () => {
|
|||
coreStart,
|
||||
SavedObjectFinder,
|
||||
notifications,
|
||||
getEmbeddableFactories
|
||||
start.getEmbeddableFactories
|
||||
);
|
||||
action.execute({ embeddable });
|
||||
});
|
||||
|
@ -99,7 +100,7 @@ test('Is not compatible when embeddable is not in a dashboard container', async
|
|||
coreStart,
|
||||
SavedObjectFinder,
|
||||
notifications,
|
||||
getEmbeddableFactories
|
||||
start.getEmbeddableFactories
|
||||
);
|
||||
expect(
|
||||
await action.isCompatible({
|
||||
|
@ -118,7 +119,7 @@ test('Execute throws an error when called with an embeddable not in a parent', a
|
|||
coreStart,
|
||||
SavedObjectFinder,
|
||||
notifications,
|
||||
getEmbeddableFactories
|
||||
start.getEmbeddableFactories
|
||||
);
|
||||
async function check() {
|
||||
await action.execute({ embeddable: container });
|
||||
|
@ -133,7 +134,7 @@ test('Returns title', async () => {
|
|||
coreStart,
|
||||
SavedObjectFinder,
|
||||
notifications,
|
||||
getEmbeddableFactories
|
||||
start.getEmbeddableFactories
|
||||
);
|
||||
expect(action.getDisplayName({ embeddable })).toBeDefined();
|
||||
});
|
||||
|
@ -145,7 +146,7 @@ test('Returns an icon', async () => {
|
|||
coreStart,
|
||||
SavedObjectFinder,
|
||||
notifications,
|
||||
getEmbeddableFactories
|
||||
start.getEmbeddableFactories
|
||||
);
|
||||
expect(action.getIconType({ embeddable })).toBeDefined();
|
||||
});
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
// @ts-ignore
|
||||
import { findTestSubject } from '@elastic/eui/lib/test';
|
||||
import { nextTick } from 'test_utils/enzyme_helpers';
|
||||
import { isErrorEmbeddable, ViewMode, EmbeddableFactory } from '../embeddable_plugin';
|
||||
import { isErrorEmbeddable, ViewMode } from '../embeddable_plugin';
|
||||
import { DashboardContainer, DashboardContainerOptions } from './dashboard_container';
|
||||
import { getSampleDashboardInput, getSampleDashboardPanel } from '../test_helpers';
|
||||
import {
|
||||
|
@ -30,14 +30,12 @@ import {
|
|||
ContactCardEmbeddable,
|
||||
ContactCardEmbeddableOutput,
|
||||
} from '../embeddable_plugin_test_samples';
|
||||
// eslint-disable-next-line
|
||||
import { embeddablePluginMock } from 'src/plugins/embeddable/public/mocks';
|
||||
|
||||
const options: DashboardContainerOptions = {
|
||||
application: {} as any,
|
||||
embeddable: {
|
||||
getTriggerCompatibleActions: (() => []) as any,
|
||||
getEmbeddableFactories: (() => []) as any,
|
||||
getEmbeddableFactory: undefined as any,
|
||||
} as any,
|
||||
embeddable: {} as any,
|
||||
notifications: {} as any,
|
||||
overlays: {} as any,
|
||||
inspector: {} as any,
|
||||
|
@ -47,12 +45,12 @@ const options: DashboardContainerOptions = {
|
|||
};
|
||||
|
||||
beforeEach(() => {
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(
|
||||
const { setup, doStart } = embeddablePluginMock.createInstance();
|
||||
setup.registerEmbeddableFactory(
|
||||
CONTACT_CARD_EMBEDDABLE,
|
||||
new ContactCardEmbeddableFactory({} as any, (() => null) as any, {} as any)
|
||||
new ContactCardEmbeddableFactory((() => null) as any, {} as any)
|
||||
);
|
||||
options.embeddable.getEmbeddableFactory = (id: string) => embeddableFactories.get(id) as any;
|
||||
options.embeddable = doStart();
|
||||
});
|
||||
|
||||
test('DashboardContainer initializes embeddables', async done => {
|
||||
|
|
|
@ -23,7 +23,7 @@ import { EmbeddableStart } from '../../../../../src/plugins/embeddable/public';
|
|||
import { CoreStart } from '../../../../core/public';
|
||||
import {
|
||||
ContainerOutput,
|
||||
EmbeddableFactory,
|
||||
EmbeddableFactoryDefinition,
|
||||
ErrorEmbeddable,
|
||||
Container,
|
||||
} from '../embeddable_plugin';
|
||||
|
@ -43,27 +43,24 @@ interface StartServices {
|
|||
uiActions: UiActionsStart;
|
||||
}
|
||||
|
||||
export class DashboardContainerFactory extends EmbeddableFactory<
|
||||
DashboardContainerInput,
|
||||
ContainerOutput
|
||||
> {
|
||||
export class DashboardContainerFactory
|
||||
implements
|
||||
EmbeddableFactoryDefinition<DashboardContainerInput, ContainerOutput, DashboardContainer> {
|
||||
public readonly isContainerType = true;
|
||||
public readonly type = DASHBOARD_CONTAINER_TYPE;
|
||||
|
||||
constructor(private readonly getStartServices: () => Promise<StartServices>) {
|
||||
super();
|
||||
}
|
||||
constructor(private readonly getStartServices: () => Promise<StartServices>) {}
|
||||
|
||||
public async isEditable() {
|
||||
public isEditable = async () => {
|
||||
const { capabilities } = await this.getStartServices();
|
||||
return !!capabilities.createNew && !!capabilities.showWriteControls;
|
||||
}
|
||||
};
|
||||
|
||||
public getDisplayName() {
|
||||
public readonly getDisplayName = () => {
|
||||
return i18n.translate('dashboard.factory.displayName', {
|
||||
defaultMessage: 'dashboard',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
public getDefaultInput(): Partial<DashboardContainerInput> {
|
||||
return {
|
||||
|
@ -73,11 +70,11 @@ export class DashboardContainerFactory extends EmbeddableFactory<
|
|||
};
|
||||
}
|
||||
|
||||
public async create(
|
||||
public create = async (
|
||||
initialInput: DashboardContainerInput,
|
||||
parent?: Container
|
||||
): Promise<DashboardContainer | ErrorEmbeddable> {
|
||||
): Promise<DashboardContainer | ErrorEmbeddable> => {
|
||||
const services = await this.getStartServices();
|
||||
return new DashboardContainer(initialInput, services, parent);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import sizeMe from 'react-sizeme';
|
|||
import React from 'react';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { skip } from 'rxjs/operators';
|
||||
import { EmbeddableFactory } from '../../embeddable_plugin';
|
||||
import { DashboardGrid, DashboardGridProps } from './dashboard_grid';
|
||||
import { DashboardContainer, DashboardContainerOptions } from '../dashboard_container';
|
||||
import { getSampleDashboardInput } from '../../test_helpers';
|
||||
|
@ -32,16 +31,20 @@ import {
|
|||
ContactCardEmbeddableFactory,
|
||||
} from '../../embeddable_plugin_test_samples';
|
||||
import { KibanaContextProvider } from '../../../../kibana_react/public';
|
||||
// eslint-disable-next-line
|
||||
import { embeddablePluginMock } from 'src/plugins/embeddable/public/mocks';
|
||||
|
||||
let dashboardContainer: DashboardContainer | undefined;
|
||||
|
||||
function prepare(props?: Partial<DashboardGridProps>) {
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(
|
||||
const { setup, doStart } = embeddablePluginMock.createInstance();
|
||||
setup.registerEmbeddableFactory(
|
||||
CONTACT_CARD_EMBEDDABLE,
|
||||
new ContactCardEmbeddableFactory({} as any, (() => {}) as any, {} as any)
|
||||
new ContactCardEmbeddableFactory((() => null) as any, {} as any)
|
||||
);
|
||||
const getEmbeddableFactory = (id: string) => embeddableFactories.get(id);
|
||||
const start = doStart();
|
||||
|
||||
const getEmbeddableFactory = start.getEmbeddableFactory;
|
||||
const initialInput = getSampleDashboardInput({
|
||||
panels: {
|
||||
'1': {
|
||||
|
@ -60,7 +63,7 @@ function prepare(props?: Partial<DashboardGridProps>) {
|
|||
application: {} as any,
|
||||
embeddable: {
|
||||
getTriggerCompatibleActions: (() => []) as any,
|
||||
getEmbeddableFactories: (() => []) as any,
|
||||
getEmbeddableFactories: start.getEmbeddableFactories,
|
||||
getEmbeddableFactory,
|
||||
} as any,
|
||||
notifications: {} as any,
|
||||
|
|
|
@ -24,7 +24,6 @@ import { skip } from 'rxjs/operators';
|
|||
import { mount } from 'enzyme';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import { nextTick } from 'test_utils/enzyme_helpers';
|
||||
import { EmbeddableFactory } from '../../embeddable_plugin';
|
||||
import { DashboardViewport, DashboardViewportProps } from './dashboard_viewport';
|
||||
import { DashboardContainer, DashboardContainerOptions } from '../dashboard_container';
|
||||
import { getSampleDashboardInput } from '../../test_helpers';
|
||||
|
@ -33,6 +32,8 @@ import {
|
|||
ContactCardEmbeddableFactory,
|
||||
} from '../../embeddable_plugin_test_samples';
|
||||
import { KibanaContextProvider } from '../../../../../plugins/kibana_react/public';
|
||||
// eslint-disable-next-line
|
||||
import { embeddablePluginMock } from 'src/plugins/embeddable/public/mocks';
|
||||
|
||||
let dashboardContainer: DashboardContainer | undefined;
|
||||
|
||||
|
@ -41,18 +42,19 @@ const ExitFullScreenButton = () => <div data-test-subj="exitFullScreenModeText">
|
|||
function getProps(
|
||||
props?: Partial<DashboardViewportProps>
|
||||
): { props: DashboardViewportProps; options: DashboardContainerOptions } {
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(
|
||||
const { setup, doStart } = embeddablePluginMock.createInstance();
|
||||
setup.registerEmbeddableFactory(
|
||||
CONTACT_CARD_EMBEDDABLE,
|
||||
new ContactCardEmbeddableFactory({}, (() => null) as any, {} as any)
|
||||
new ContactCardEmbeddableFactory((() => null) as any, {} as any)
|
||||
);
|
||||
|
||||
const start = doStart();
|
||||
const options: DashboardContainerOptions = {
|
||||
application: {} as any,
|
||||
embeddable: {
|
||||
getTriggerCompatibleActions: (() => []) as any,
|
||||
getEmbeddableFactories: (() => []) as any,
|
||||
getEmbeddableFactory: (id: string) => embeddableFactories.get(id),
|
||||
getEmbeddableFactories: start.getEmbeddableFactories,
|
||||
getEmbeddableFactory: start.getEmbeddableFactory,
|
||||
} as any,
|
||||
notifications: {} as any,
|
||||
overlays: {} as any,
|
||||
|
|
|
@ -52,7 +52,7 @@ test('DashboardContainer in edit mode shows edit mode actions', async () => {
|
|||
uiActionsSetup.attachAction(CONTEXT_MENU_TRIGGER, editModeAction);
|
||||
setup.registerEmbeddableFactory(
|
||||
CONTACT_CARD_EMBEDDABLE,
|
||||
new ContactCardEmbeddableFactory({} as any, (() => null) as any, {} as any)
|
||||
new ContactCardEmbeddableFactory((() => null) as any, {} as any)
|
||||
);
|
||||
|
||||
const start = doStart();
|
||||
|
|
|
@ -38,6 +38,7 @@ export {
|
|||
EmbeddableChildPanel,
|
||||
EmbeddableChildPanelProps,
|
||||
EmbeddableContext,
|
||||
EmbeddableFactoryDefinition,
|
||||
EmbeddableFactory,
|
||||
EmbeddableFactoryNotFoundError,
|
||||
EmbeddableFactoryRenderer,
|
||||
|
|
|
@ -18,14 +18,14 @@
|
|||
*/
|
||||
|
||||
import { EditPanelAction } from './edit_panel_action';
|
||||
import { EmbeddableFactory, Embeddable, EmbeddableInput } from '../embeddables';
|
||||
import { Embeddable, EmbeddableInput } from '../embeddables';
|
||||
import { ViewMode } from '../types';
|
||||
import { ContactCardEmbeddable } from '../test_samples';
|
||||
import { EmbeddableStart } from '../../plugin';
|
||||
import { embeddablePluginMock } from '../../mocks';
|
||||
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
const getFactory = ((id: string) =>
|
||||
embeddableFactories.get(id)) as EmbeddableStart['getEmbeddableFactory'];
|
||||
const { doStart } = embeddablePluginMock.createInstance();
|
||||
const start = doStart();
|
||||
const getFactory = start.getEmbeddableFactory;
|
||||
|
||||
class EditableEmbeddable extends Embeddable {
|
||||
public readonly type = 'EDITABLE_EMBEDDABLE';
|
||||
|
@ -83,9 +83,7 @@ test('is not compatible when edit url is not available', async () => {
|
|||
});
|
||||
|
||||
test('is not visible when edit url is available but in view mode', async () => {
|
||||
embeddableFactories.clear();
|
||||
const action = new EditPanelAction((type =>
|
||||
embeddableFactories.get(type)) as EmbeddableStart['getEmbeddableFactory']);
|
||||
const action = new EditPanelAction(getFactory);
|
||||
expect(
|
||||
await action.isCompatible({
|
||||
embeddable: new EditableEmbeddable(
|
||||
|
@ -100,9 +98,7 @@ test('is not visible when edit url is available but in view mode', async () => {
|
|||
});
|
||||
|
||||
test('is not compatible when edit url is available, in edit mode, but not editable', async () => {
|
||||
embeddableFactories.clear();
|
||||
const action = new EditPanelAction((type =>
|
||||
embeddableFactories.get(type)) as EmbeddableStart['getEmbeddableFactory']);
|
||||
const action = new EditPanelAction(getFactory);
|
||||
expect(
|
||||
await action.isCompatible({
|
||||
embeddable: new EditableEmbeddable(
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
import React from 'react';
|
||||
import { nextTick } from 'test_utils/enzyme_helpers';
|
||||
import { EmbeddableChildPanel } from './embeddable_child_panel';
|
||||
import { EmbeddableFactory } from '../embeddables';
|
||||
import { CONTACT_CARD_EMBEDDABLE } from '../test_samples/embeddables/contact_card/contact_card_embeddable_factory';
|
||||
import { SlowContactCardEmbeddableFactory } from '../test_samples/embeddables/contact_card/slow_contact_card_embeddable_factory';
|
||||
import { HelloWorldContainer } from '../test_samples/embeddables/hello_world_container';
|
||||
|
@ -32,16 +31,17 @@ import {
|
|||
// eslint-disable-next-line
|
||||
import { inspectorPluginMock } from 'src/plugins/inspector/public/mocks';
|
||||
import { mount } from 'enzyme';
|
||||
import { embeddablePluginMock } from '../../mocks';
|
||||
|
||||
test('EmbeddableChildPanel renders an embeddable when it is done loading', async () => {
|
||||
const inspector = inspectorPluginMock.createStartContract();
|
||||
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(
|
||||
const { setup, doStart } = embeddablePluginMock.createInstance();
|
||||
setup.registerEmbeddableFactory(
|
||||
CONTACT_CARD_EMBEDDABLE,
|
||||
new SlowContactCardEmbeddableFactory({ execAction: (() => null) as any })
|
||||
);
|
||||
const getEmbeddableFactory = (id: string) => embeddableFactories.get(id);
|
||||
const start = doStart();
|
||||
const getEmbeddableFactory = start.getEmbeddableFactory;
|
||||
|
||||
const container = new HelloWorldContainer({ id: 'hello', panels: {} }, {
|
||||
getEmbeddableFactory,
|
||||
|
@ -63,8 +63,8 @@ test('EmbeddableChildPanel renders an embeddable when it is done loading', async
|
|||
container={container}
|
||||
embeddableId={newEmbeddable.id}
|
||||
getActions={() => Promise.resolve([])}
|
||||
getAllEmbeddableFactories={(() => []) as any}
|
||||
getEmbeddableFactory={(() => undefined) as any}
|
||||
getAllEmbeddableFactories={start.getEmbeddableFactories}
|
||||
getEmbeddableFactory={getEmbeddableFactory}
|
||||
notifications={{} as any}
|
||||
overlays={{} as any}
|
||||
inspector={inspector}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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 { SavedObjectAttributes } from 'kibana/public';
|
||||
import { EmbeddableFactoryDefinition } from './embeddable_factory_definition';
|
||||
import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from './i_embeddable';
|
||||
import { EmbeddableFactory } from './embeddable_factory';
|
||||
import { IContainer } from '..';
|
||||
|
||||
export const defaultEmbeddableFactoryProvider = <
|
||||
I extends EmbeddableInput = EmbeddableInput,
|
||||
O extends EmbeddableOutput = EmbeddableOutput,
|
||||
E extends IEmbeddable<I, O> = IEmbeddable<I, O>,
|
||||
T extends SavedObjectAttributes = SavedObjectAttributes
|
||||
>(
|
||||
def: EmbeddableFactoryDefinition<I, O, E, T>
|
||||
): EmbeddableFactory<I, O, E, T> => {
|
||||
const factory: EmbeddableFactory<I, O, E, T> = {
|
||||
isContainerType: def.isContainerType ?? false,
|
||||
canCreateNew: def.canCreateNew ? def.canCreateNew.bind(def) : () => true,
|
||||
getDefaultInput: def.getDefaultInput ? def.getDefaultInput.bind(def) : () => ({}),
|
||||
getExplicitInput: def.getExplicitInput
|
||||
? def.getExplicitInput.bind(def)
|
||||
: () => Promise.resolve({}),
|
||||
createFromSavedObject:
|
||||
def.createFromSavedObject ??
|
||||
((savedObjectId: string, input: Partial<I>, parent?: IContainer) => {
|
||||
throw new Error(`Creation from saved object not supported by type ${def.type}`);
|
||||
}),
|
||||
create: def.create.bind(def),
|
||||
type: def.type,
|
||||
isEditable: def.isEditable.bind(def),
|
||||
getDisplayName: def.getDisplayName.bind(def),
|
||||
savedObjectMetaData: def.savedObjectMetaData,
|
||||
};
|
||||
return factory;
|
||||
};
|
|
@ -158,6 +158,8 @@ export abstract class Embeddable<
|
|||
*/
|
||||
public destroy(): void {
|
||||
this.destoyed = true;
|
||||
this.input$.complete();
|
||||
this.output$.complete();
|
||||
if (this.parentSubscription) {
|
||||
this.parentSubscription.unsubscribe();
|
||||
}
|
||||
|
|
|
@ -22,32 +22,21 @@ import { SavedObjectMetaData } from '../../../../saved_objects/public';
|
|||
import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from './i_embeddable';
|
||||
import { ErrorEmbeddable } from './error_embeddable';
|
||||
import { IContainer } from '../containers/i_container';
|
||||
import { PropertySpec } from '../types';
|
||||
|
||||
export interface EmbeddableInstanceConfiguration {
|
||||
id: string;
|
||||
savedObjectId?: string;
|
||||
}
|
||||
|
||||
export interface PropertySpec {
|
||||
displayName: string;
|
||||
accessPath: string;
|
||||
id: string;
|
||||
description: string;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export interface OutputSpec {
|
||||
[key: string]: PropertySpec;
|
||||
}
|
||||
|
||||
export interface EmbeddableFactoryOptions<T extends SavedObjectAttributes> {
|
||||
savedObjectMetaData?: SavedObjectMetaData<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The EmbeddableFactory creates and initializes an embeddable instance
|
||||
* EmbeddableFactories create and initialize an embeddable instance
|
||||
*/
|
||||
export abstract class EmbeddableFactory<
|
||||
export interface EmbeddableFactory<
|
||||
TEmbeddableInput extends EmbeddableInput = EmbeddableInput,
|
||||
TEmbeddableOutput extends EmbeddableOutput = EmbeddableOutput,
|
||||
TEmbeddable extends IEmbeddable<TEmbeddableInput, TEmbeddableOutput> = IEmbeddable<
|
||||
|
@ -58,9 +47,15 @@ export abstract class EmbeddableFactory<
|
|||
> {
|
||||
// A unique identified for this factory, which will be used to map an embeddable spec to
|
||||
// a factory that can generate an instance of it.
|
||||
public abstract readonly type: string;
|
||||
readonly type: string;
|
||||
|
||||
public readonly savedObjectMetaData?: SavedObjectMetaData<TSavedObjectAttributes>;
|
||||
/**
|
||||
* Returns whether the current user should be allowed to edit this type of
|
||||
* embeddable. Most of the time this should be based off the capabilities service, hence it's async.
|
||||
*/
|
||||
readonly isEditable: () => Promise<boolean>;
|
||||
|
||||
readonly savedObjectMetaData?: SavedObjectMetaData<TSavedObjectAttributes>;
|
||||
|
||||
/**
|
||||
* True if is this factory create embeddables that are Containers. Used in the add panel to
|
||||
|
@ -68,31 +63,19 @@ export abstract class EmbeddableFactory<
|
|||
* supported right now, but once nested containers are officially supported we can probably get
|
||||
* rid of this interface.
|
||||
*/
|
||||
public readonly isContainerType: boolean = false;
|
||||
|
||||
constructor({ savedObjectMetaData }: EmbeddableFactoryOptions<TSavedObjectAttributes> = {}) {
|
||||
this.savedObjectMetaData = savedObjectMetaData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the current user should be allowed to edit this type of
|
||||
* embeddable. Most of the time this should be based off the capabilities service, hence it's async.
|
||||
*/
|
||||
public abstract async isEditable(): Promise<boolean>;
|
||||
readonly isContainerType: boolean;
|
||||
|
||||
/**
|
||||
* Returns a display name for this type of embeddable. Used in "Create new... " options
|
||||
* in the add panel for containers.
|
||||
*/
|
||||
public abstract getDisplayName(): string;
|
||||
getDisplayName(): string;
|
||||
|
||||
/**
|
||||
* If false, this type of embeddable can't be created with the "createNew" functionality. Instead,
|
||||
* use createFromSavedObject, where an existing saved object must first exist.
|
||||
*/
|
||||
public canCreateNew() {
|
||||
return true;
|
||||
}
|
||||
canCreateNew(): boolean;
|
||||
|
||||
/**
|
||||
* Can be used to get any default input, to be passed in to during the creation process. Default
|
||||
|
@ -100,18 +83,14 @@ export abstract class EmbeddableFactory<
|
|||
* default input parameters.
|
||||
* @param partial
|
||||
*/
|
||||
public getDefaultInput(partial: Partial<TEmbeddableInput>): Partial<TEmbeddableInput> {
|
||||
return {};
|
||||
}
|
||||
getDefaultInput(partial: Partial<TEmbeddableInput>): Partial<TEmbeddableInput>;
|
||||
|
||||
/**
|
||||
* Can be used to request explicit input from the user, to be passed in to `EmbeddableFactory:create`.
|
||||
* Explicit input is stored on the parent container for this embeddable. It overrides any inherited
|
||||
* input passed down from the parent container.
|
||||
*/
|
||||
public async getExplicitInput(): Promise<Partial<TEmbeddableInput>> {
|
||||
return {};
|
||||
}
|
||||
getExplicitInput(): Promise<Partial<TEmbeddableInput>>;
|
||||
|
||||
/**
|
||||
* Creates a new embeddable instance based off the saved object id.
|
||||
|
@ -120,13 +99,11 @@ export abstract class EmbeddableFactory<
|
|||
* range of the parent container.
|
||||
* @param parent
|
||||
*/
|
||||
public createFromSavedObject(
|
||||
createFromSavedObject(
|
||||
savedObjectId: string,
|
||||
input: Partial<TEmbeddableInput>,
|
||||
parent?: IContainer
|
||||
): Promise<TEmbeddable | ErrorEmbeddable> {
|
||||
throw new Error(`Creation from saved object not supported by type ${this.type}`);
|
||||
}
|
||||
): Promise<TEmbeddable | ErrorEmbeddable>;
|
||||
|
||||
/**
|
||||
* Resolves to undefined if a new Embeddable cannot be directly created and the user will instead be redirected
|
||||
|
@ -134,7 +111,7 @@ export abstract class EmbeddableFactory<
|
|||
*
|
||||
* This will likely change in future iterations when we improve in place editing capabilities.
|
||||
*/
|
||||
public abstract create(
|
||||
create(
|
||||
initialInput: TEmbeddableInput,
|
||||
parent?: IContainer
|
||||
): Promise<TEmbeddable | ErrorEmbeddable | undefined>;
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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 { SavedObjectAttributes } from 'kibana/server';
|
||||
import { IEmbeddable } from './i_embeddable';
|
||||
import { EmbeddableFactory } from './embeddable_factory';
|
||||
import { EmbeddableInput, EmbeddableOutput } from '..';
|
||||
|
||||
export type EmbeddableFactoryDefinition<
|
||||
I extends EmbeddableInput = EmbeddableInput,
|
||||
O extends EmbeddableOutput = EmbeddableOutput,
|
||||
E extends IEmbeddable<I, O> = IEmbeddable<I, O>,
|
||||
T extends SavedObjectAttributes = SavedObjectAttributes
|
||||
> =
|
||||
// Required parameters
|
||||
Pick<EmbeddableFactory<I, O, E, T>, 'create' | 'type' | 'isEditable' | 'getDisplayName'> &
|
||||
// Optional parameters
|
||||
Partial<
|
||||
Pick<
|
||||
EmbeddableFactory<I, O, E, T>,
|
||||
| 'createFromSavedObject'
|
||||
| 'isContainerType'
|
||||
| 'getExplicitInput'
|
||||
| 'savedObjectMetaData'
|
||||
| 'canCreateNew'
|
||||
| 'getDefaultInput'
|
||||
>
|
||||
>;
|
|
@ -21,22 +21,22 @@ import {
|
|||
HELLO_WORLD_EMBEDDABLE,
|
||||
HelloWorldEmbeddableFactory,
|
||||
} from '../../../../../../examples/embeddable_examples/public';
|
||||
import { EmbeddableFactory } from './embeddable_factory';
|
||||
import { EmbeddableFactoryRenderer } from './embeddable_factory_renderer';
|
||||
import { mount } from 'enzyme';
|
||||
import { nextTick } from 'test_utils/enzyme_helpers';
|
||||
// @ts-ignore
|
||||
import { findTestSubject } from '@elastic/eui/lib/test';
|
||||
import { EmbeddableStart } from '../../plugin';
|
||||
import { embeddablePluginMock } from '../../mocks';
|
||||
|
||||
test('EmbeddableFactoryRenderer renders an embeddable', async () => {
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(HELLO_WORLD_EMBEDDABLE, new HelloWorldEmbeddableFactory());
|
||||
const getEmbeddableFactory = (id: string) => embeddableFactories.get(id);
|
||||
const { setup, doStart } = embeddablePluginMock.createInstance();
|
||||
setup.registerEmbeddableFactory(HELLO_WORLD_EMBEDDABLE, new HelloWorldEmbeddableFactory());
|
||||
|
||||
const getEmbeddableFactory = doStart().getEmbeddableFactory;
|
||||
|
||||
const component = mount(
|
||||
<EmbeddableFactoryRenderer
|
||||
getEmbeddableFactory={getEmbeddableFactory as EmbeddableStart['getEmbeddableFactory']}
|
||||
getEmbeddableFactory={getEmbeddableFactory}
|
||||
type={HELLO_WORLD_EMBEDDABLE}
|
||||
input={{ id: '123' }}
|
||||
/>
|
||||
|
|
|
@ -18,11 +18,9 @@
|
|||
*/
|
||||
export { EmbeddableOutput, EmbeddableInput, IEmbeddable } from './i_embeddable';
|
||||
export { Embeddable } from './embeddable';
|
||||
export {
|
||||
EmbeddableInstanceConfiguration,
|
||||
EmbeddableFactory,
|
||||
OutputSpec,
|
||||
} from './embeddable_factory';
|
||||
export * from './embeddable_factory';
|
||||
export * from './embeddable_factory_definition';
|
||||
export * from './default_embeddable_factory_provider';
|
||||
export { ErrorEmbeddable, isErrorEmbeddable } from './error_embeddable';
|
||||
export { withEmbeddableSubscription } from './with_subscription';
|
||||
export { EmbeddableFactoryRenderer } from './embeddable_factory_renderer';
|
||||
|
|
|
@ -27,7 +27,7 @@ import { I18nProvider } from '@kbn/i18n/react';
|
|||
import { CONTEXT_MENU_TRIGGER } from '../triggers';
|
||||
import { Action, UiActionsStart, ActionType } from 'src/plugins/ui_actions/public';
|
||||
import { Trigger, ViewMode } from '../types';
|
||||
import { EmbeddableFactory, isErrorEmbeddable } from '../embeddables';
|
||||
import { isErrorEmbeddable } from '../embeddables';
|
||||
import { EmbeddablePanel } from './embeddable_panel';
|
||||
import { createEditModeAction } from '../test_samples/actions';
|
||||
import {
|
||||
|
@ -43,26 +43,25 @@ import {
|
|||
// eslint-disable-next-line
|
||||
import { inspectorPluginMock } from 'src/plugins/inspector/public/mocks';
|
||||
import { EuiBadge } from '@elastic/eui';
|
||||
import { embeddablePluginMock } from '../../mocks';
|
||||
|
||||
const actionRegistry = new Map<string, Action<object | undefined | string | number>>();
|
||||
const triggerRegistry = new Map<string, Trigger>();
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
const getEmbeddableFactory = (id: string) => embeddableFactories.get(id);
|
||||
|
||||
const { setup, doStart } = embeddablePluginMock.createInstance();
|
||||
|
||||
const editModeAction = createEditModeAction();
|
||||
const trigger: Trigger = {
|
||||
id: CONTEXT_MENU_TRIGGER,
|
||||
};
|
||||
const embeddableFactory = new ContactCardEmbeddableFactory(
|
||||
{} as any,
|
||||
(() => null) as any,
|
||||
{} as any
|
||||
);
|
||||
const embeddableFactory = new ContactCardEmbeddableFactory((() => null) as any, {} as any);
|
||||
|
||||
actionRegistry.set(editModeAction.id, editModeAction);
|
||||
triggerRegistry.set(trigger.id, trigger);
|
||||
embeddableFactories.set(embeddableFactory.type, embeddableFactory);
|
||||
setup.registerEmbeddableFactory(embeddableFactory.type, embeddableFactory);
|
||||
|
||||
const start = doStart();
|
||||
const getEmbeddableFactory = start.getEmbeddableFactory;
|
||||
test('HelloWorldContainer initializes embeddables', async done => {
|
||||
const container = new HelloWorldContainer(
|
||||
{
|
||||
|
@ -157,8 +156,8 @@ test('HelloWorldContainer in view mode hides edit mode actions', async () => {
|
|||
<EmbeddablePanel
|
||||
embeddable={embeddable}
|
||||
getActions={() => Promise.resolve([])}
|
||||
getAllEmbeddableFactories={(() => []) as any}
|
||||
getEmbeddableFactory={(() => undefined) as any}
|
||||
getAllEmbeddableFactories={start.getEmbeddableFactories}
|
||||
getEmbeddableFactory={start.getEmbeddableFactory}
|
||||
notifications={{} as any}
|
||||
overlays={{} as any}
|
||||
inspector={inspector}
|
||||
|
@ -195,8 +194,8 @@ const renderInEditModeAndOpenContextMenu = async (
|
|||
<EmbeddablePanel
|
||||
embeddable={embeddable}
|
||||
getActions={getActions}
|
||||
getAllEmbeddableFactories={(() => []) as any}
|
||||
getEmbeddableFactory={(() => undefined) as any}
|
||||
getAllEmbeddableFactories={start.getEmbeddableFactories}
|
||||
getEmbeddableFactory={start.getEmbeddableFactory}
|
||||
notifications={{} as any}
|
||||
overlays={{} as any}
|
||||
inspector={inspector}
|
||||
|
@ -293,8 +292,8 @@ test('HelloWorldContainer in edit mode shows edit mode actions', async () => {
|
|||
<EmbeddablePanel
|
||||
embeddable={embeddable}
|
||||
getActions={() => Promise.resolve([])}
|
||||
getAllEmbeddableFactories={(() => []) as any}
|
||||
getEmbeddableFactory={(() => undefined) as any}
|
||||
getAllEmbeddableFactories={start.getEmbeddableFactories}
|
||||
getEmbeddableFactory={start.getEmbeddableFactory}
|
||||
notifications={{} as any}
|
||||
overlays={{} as any}
|
||||
inspector={inspector}
|
||||
|
@ -355,8 +354,8 @@ test('Updates when hidePanelTitles is toggled', async () => {
|
|||
<EmbeddablePanel
|
||||
embeddable={embeddable}
|
||||
getActions={() => Promise.resolve([])}
|
||||
getAllEmbeddableFactories={(() => []) as any}
|
||||
getEmbeddableFactory={(() => undefined) as any}
|
||||
getAllEmbeddableFactories={start.getEmbeddableFactories}
|
||||
getEmbeddableFactory={start.getEmbeddableFactory}
|
||||
notifications={{} as any}
|
||||
overlays={{} as any}
|
||||
inspector={inspector}
|
||||
|
@ -407,8 +406,8 @@ test('Check when hide header option is false', async () => {
|
|||
<EmbeddablePanel
|
||||
embeddable={embeddable}
|
||||
getActions={() => Promise.resolve([])}
|
||||
getAllEmbeddableFactories={(() => []) as any}
|
||||
getEmbeddableFactory={(() => undefined) as any}
|
||||
getAllEmbeddableFactories={start.getEmbeddableFactories}
|
||||
getEmbeddableFactory={start.getEmbeddableFactory}
|
||||
notifications={{} as any}
|
||||
overlays={{} as any}
|
||||
inspector={inspector}
|
||||
|
@ -444,8 +443,8 @@ test('Check when hide header option is true', async () => {
|
|||
<EmbeddablePanel
|
||||
embeddable={embeddable}
|
||||
getActions={() => Promise.resolve([])}
|
||||
getAllEmbeddableFactories={(() => []) as any}
|
||||
getEmbeddableFactory={(() => undefined) as any}
|
||||
getAllEmbeddableFactories={start.getEmbeddableFactories}
|
||||
getEmbeddableFactory={start.getEmbeddableFactory}
|
||||
notifications={{} as any}
|
||||
overlays={{} as any}
|
||||
inspector={inspector}
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
import { ViewMode, EmbeddableOutput, isErrorEmbeddable } from '../../../../';
|
||||
import { AddPanelAction } from './add_panel_action';
|
||||
import { EmbeddableFactory } from '../../../../embeddables';
|
||||
import {
|
||||
FILTERABLE_EMBEDDABLE,
|
||||
FilterableEmbeddable,
|
||||
|
@ -31,11 +30,12 @@ import { FilterableContainer } from '../../../../test_samples/embeddables/filter
|
|||
import { coreMock } from '../../../../../../../../core/public/mocks';
|
||||
import { ContactCardEmbeddable } from '../../../../test_samples';
|
||||
import { esFilters, Filter } from '../../../../../../../../plugins/data/public';
|
||||
import { EmbeddableStart } from 'src/plugins/embeddable/public/plugin';
|
||||
import { EmbeddableStart } from '../../../../../plugin';
|
||||
import { embeddablePluginMock } from '../../../../../mocks';
|
||||
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory());
|
||||
const getFactory = (id: string) => embeddableFactories.get(id);
|
||||
const { setup, doStart } = embeddablePluginMock.createInstance();
|
||||
setup.registerEmbeddableFactory(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory());
|
||||
const getFactory = doStart().getEmbeddableFactory;
|
||||
|
||||
let container: FilterableContainer;
|
||||
let embeddable: FilterableEmbeddable;
|
||||
|
|
|
@ -31,7 +31,7 @@ import { ReactWrapper } from 'enzyme';
|
|||
import { coreMock } from '../../../../../../../../core/public/mocks';
|
||||
// @ts-ignore
|
||||
import { findTestSubject } from '@elastic/eui/lib/test';
|
||||
import { EmbeddableStart } from 'src/plugins/embeddable/public/plugin';
|
||||
import { embeddablePluginMock } from '../../../../../mocks';
|
||||
|
||||
function DummySavedObjectFinder(props: { children: React.ReactNode }) {
|
||||
return (
|
||||
|
@ -43,10 +43,10 @@ function DummySavedObjectFinder(props: { children: React.ReactNode }) {
|
|||
}
|
||||
|
||||
test('createNewEmbeddable() add embeddable to container', async () => {
|
||||
const { setup, doStart } = embeddablePluginMock.createInstance();
|
||||
const core = coreMock.createStart();
|
||||
const { overlays } = core;
|
||||
const contactCardEmbeddableFactory = new ContactCardEmbeddableFactory(
|
||||
{},
|
||||
(() => null) as any,
|
||||
overlays
|
||||
);
|
||||
|
@ -55,7 +55,9 @@ test('createNewEmbeddable() add embeddable to container', async () => {
|
|||
firstName: 'foo',
|
||||
lastName: 'bar',
|
||||
} as any);
|
||||
const getEmbeddableFactory = (id: string) => contactCardEmbeddableFactory;
|
||||
setup.registerEmbeddableFactory(CONTACT_CARD_EMBEDDABLE, contactCardEmbeddableFactory);
|
||||
const start = doStart();
|
||||
const getEmbeddableFactory = start.getEmbeddableFactory;
|
||||
const input: ContainerInput<{ firstName: string; lastName: string }> = {
|
||||
id: '1',
|
||||
panels: {},
|
||||
|
@ -66,8 +68,8 @@ test('createNewEmbeddable() add embeddable to container', async () => {
|
|||
<AddPanelFlyout
|
||||
container={container}
|
||||
onClose={onClose}
|
||||
getFactory={getEmbeddableFactory as EmbeddableStart['getEmbeddableFactory']}
|
||||
getAllFactories={() => new Set<any>([contactCardEmbeddableFactory]).values()}
|
||||
getFactory={getEmbeddableFactory}
|
||||
getAllFactories={start.getEmbeddableFactories}
|
||||
notifications={core.notifications}
|
||||
SavedObjectFinder={() => null}
|
||||
/>
|
||||
|
@ -88,10 +90,10 @@ test('createNewEmbeddable() add embeddable to container', async () => {
|
|||
});
|
||||
|
||||
test('selecting embeddable in "Create new ..." list calls createNewEmbeddable()', async () => {
|
||||
const { setup, doStart } = embeddablePluginMock.createInstance();
|
||||
const core = coreMock.createStart();
|
||||
const { overlays } = core;
|
||||
const contactCardEmbeddableFactory = new ContactCardEmbeddableFactory(
|
||||
{},
|
||||
(() => null) as any,
|
||||
overlays
|
||||
);
|
||||
|
@ -100,8 +102,10 @@ test('selecting embeddable in "Create new ..." list calls createNewEmbeddable()'
|
|||
firstName: 'foo',
|
||||
lastName: 'bar',
|
||||
} as any);
|
||||
const getEmbeddableFactory = ((id: string) =>
|
||||
contactCardEmbeddableFactory) as EmbeddableStart['getEmbeddableFactory'];
|
||||
|
||||
setup.registerEmbeddableFactory(CONTACT_CARD_EMBEDDABLE, contactCardEmbeddableFactory);
|
||||
const start = doStart();
|
||||
const getEmbeddableFactory = start.getEmbeddableFactory;
|
||||
const input: ContainerInput<{ firstName: string; lastName: string }> = {
|
||||
id: '1',
|
||||
panels: {},
|
||||
|
@ -113,7 +117,7 @@ test('selecting embeddable in "Create new ..." list calls createNewEmbeddable()'
|
|||
container={container}
|
||||
onClose={onClose}
|
||||
getFactory={getEmbeddableFactory}
|
||||
getAllFactories={() => new Set<any>([contactCardEmbeddableFactory]).values()}
|
||||
getAllFactories={start.getEmbeddableFactories}
|
||||
notifications={core.notifications}
|
||||
SavedObjectFinder={props => <DummySavedObjectFinder {...props} />}
|
||||
/>
|
||||
|
|
|
@ -121,15 +121,16 @@ export class AddPanelFlyout extends React.Component<Props, State> {
|
|||
|
||||
public render() {
|
||||
const SavedObjectFinder = this.props.SavedObjectFinder;
|
||||
const metaData = [...this.props.getAllFactories()]
|
||||
.filter(
|
||||
embeddableFactory =>
|
||||
Boolean(embeddableFactory.savedObjectMetaData) && !embeddableFactory.isContainerType
|
||||
)
|
||||
.map(({ savedObjectMetaData }) => savedObjectMetaData as any);
|
||||
const savedObjectsFinder = (
|
||||
<SavedObjectFinder
|
||||
onChoose={this.onAddPanel}
|
||||
savedObjectMetaData={[...this.props.getAllFactories()]
|
||||
.filter(
|
||||
embeddableFactory =>
|
||||
Boolean(embeddableFactory.savedObjectMetaData) && !embeddableFactory.isContainerType
|
||||
)
|
||||
.map(({ savedObjectMetaData }) => savedObjectMetaData as any)}
|
||||
savedObjectMetaData={metaData}
|
||||
showFilter={true}
|
||||
noItemsMessage={i18n.translate('embeddableApi.addPanel.noMatchingObjectsMessage', {
|
||||
defaultMessage: 'No matching objects found.',
|
||||
|
|
|
@ -32,18 +32,19 @@ import {
|
|||
ContactCardEmbeddableFactory,
|
||||
} from '../../../../test_samples/embeddables/contact_card/contact_card_embeddable_factory';
|
||||
import { HelloWorldContainer } from '../../../../test_samples/embeddables/hello_world_container';
|
||||
import { EmbeddableFactory } from '../../../../embeddables';
|
||||
import { embeddablePluginMock } from '../../../../../mocks';
|
||||
|
||||
let container: Container;
|
||||
let embeddable: ContactCardEmbeddable;
|
||||
|
||||
function createHelloWorldContainer(input = { id: '123', panels: {} }) {
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
const getEmbeddableFactory = (id: string) => embeddableFactories.get(id);
|
||||
embeddableFactories.set(
|
||||
const { setup, doStart } = embeddablePluginMock.createInstance();
|
||||
setup.registerEmbeddableFactory(
|
||||
CONTACT_CARD_EMBEDDABLE,
|
||||
new ContactCardEmbeddableFactory({}, (() => {}) as any, {} as any)
|
||||
new ContactCardEmbeddableFactory((() => {}) as any, {} as any)
|
||||
);
|
||||
const getEmbeddableFactory = doStart().getEmbeddableFactory;
|
||||
|
||||
return new HelloWorldContainer(input, { getEmbeddableFactory } as any);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,20 +28,16 @@ import {
|
|||
} from '../../../test_samples';
|
||||
// eslint-disable-next-line
|
||||
import { inspectorPluginMock } from 'src/plugins/inspector/public/mocks';
|
||||
import {
|
||||
EmbeddableFactory,
|
||||
EmbeddableOutput,
|
||||
isErrorEmbeddable,
|
||||
ErrorEmbeddable,
|
||||
} from '../../../embeddables';
|
||||
import { EmbeddableOutput, isErrorEmbeddable, ErrorEmbeddable } from '../../../embeddables';
|
||||
import { of } from '../../../../tests/helpers';
|
||||
import { esFilters } from '../../../../../../../plugins/data/public';
|
||||
import { EmbeddableStart } from 'src/plugins/embeddable/public/plugin';
|
||||
import { embeddablePluginMock } from '../../../../mocks';
|
||||
import { EmbeddableStart } from '../../../../plugin';
|
||||
|
||||
const setup = async () => {
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory());
|
||||
const getFactory = (id: string) => embeddableFactories.get(id);
|
||||
const setupTests = async () => {
|
||||
const { setup, doStart } = embeddablePluginMock.createInstance();
|
||||
setup.registerEmbeddableFactory(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory());
|
||||
const getFactory = doStart().getEmbeddableFactory;
|
||||
const container = new FilterableContainer(
|
||||
{
|
||||
id: 'hello',
|
||||
|
@ -79,7 +75,7 @@ test('Is compatible when inspector adapters are available', async () => {
|
|||
const inspector = inspectorPluginMock.createStartContract();
|
||||
inspector.isAvailable.mockImplementation(() => true);
|
||||
|
||||
const { embeddable } = await setup();
|
||||
const { embeddable } = await setupTests();
|
||||
const inspectAction = new InspectPanelAction(inspector);
|
||||
|
||||
expect(await inspectAction.isCompatible({ embeddable })).toBe(true);
|
||||
|
@ -114,7 +110,7 @@ test('Executes when inspector adapters are available', async () => {
|
|||
const inspector = inspectorPluginMock.createStartContract();
|
||||
inspector.isAvailable.mockImplementation(() => true);
|
||||
|
||||
const { embeddable } = await setup();
|
||||
const { embeddable } = await setupTests();
|
||||
const inspectAction = new InspectPanelAction(inspector);
|
||||
|
||||
expect(inspector.open).toHaveBeenCalledTimes(0);
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
import { EmbeddableOutput, isErrorEmbeddable } from '../../../';
|
||||
import { RemovePanelAction } from './remove_panel_action';
|
||||
import { EmbeddableFactory } from '../../../embeddables';
|
||||
import { EmbeddableStart } from '../../../../plugin';
|
||||
import {
|
||||
FILTERABLE_EMBEDDABLE,
|
||||
|
@ -31,11 +30,11 @@ import { FilterableContainer } from '../../../test_samples/embeddables/filterabl
|
|||
import { ViewMode } from '../../../types';
|
||||
import { ContactCardEmbeddable } from '../../../test_samples/embeddables/contact_card/contact_card_embeddable';
|
||||
import { esFilters, Filter } from '../../../../../../../plugins/data/public';
|
||||
import { embeddablePluginMock } from '../../../../mocks';
|
||||
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory());
|
||||
const getFactory = (id: string) => embeddableFactories.get(id);
|
||||
|
||||
const { setup, doStart } = embeddablePluginMock.createInstance();
|
||||
setup.registerEmbeddableFactory(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory());
|
||||
const getFactory = doStart().getEmbeddableFactory;
|
||||
let container: FilterableContainer;
|
||||
let embeddable: FilterableEmbeddable;
|
||||
|
||||
|
|
|
@ -23,24 +23,21 @@ import { UiActionsStart } from 'src/plugins/ui_actions/public';
|
|||
|
||||
import { CoreStart } from 'src/core/public';
|
||||
import { toMountPoint } from '../../../../../../kibana_react/public';
|
||||
import { EmbeddableFactory } from '../../../embeddables';
|
||||
import { EmbeddableFactoryDefinition } from '../../../embeddables';
|
||||
import { Container } from '../../../containers';
|
||||
import { ContactCardEmbeddable, ContactCardEmbeddableInput } from './contact_card_embeddable';
|
||||
import { ContactCardInitializer } from './contact_card_initializer';
|
||||
import { EmbeddableFactoryOptions } from '../../../embeddables/embeddable_factory';
|
||||
|
||||
export const CONTACT_CARD_EMBEDDABLE = 'CONTACT_CARD_EMBEDDABLE';
|
||||
|
||||
export class ContactCardEmbeddableFactory extends EmbeddableFactory<ContactCardEmbeddableInput> {
|
||||
export class ContactCardEmbeddableFactory
|
||||
implements EmbeddableFactoryDefinition<ContactCardEmbeddableInput> {
|
||||
public readonly type = CONTACT_CARD_EMBEDDABLE;
|
||||
|
||||
constructor(
|
||||
options: EmbeddableFactoryOptions<any>,
|
||||
private readonly execTrigger: UiActionsStart['executeTriggerActions'],
|
||||
private readonly overlays: CoreStart['overlays']
|
||||
) {
|
||||
super(options);
|
||||
}
|
||||
) {}
|
||||
|
||||
public async isEditable() {
|
||||
return true;
|
||||
|
@ -52,7 +49,7 @@ export class ContactCardEmbeddableFactory extends EmbeddableFactory<ContactCardE
|
|||
});
|
||||
}
|
||||
|
||||
public getExplicitInput(): Promise<Partial<ContactCardEmbeddableInput>> {
|
||||
public getExplicitInput = (): Promise<Partial<ContactCardEmbeddableInput>> => {
|
||||
return new Promise(resolve => {
|
||||
const modalSession = this.overlays.openModal(
|
||||
toMountPoint(
|
||||
|
@ -72,9 +69,9 @@ export class ContactCardEmbeddableFactory extends EmbeddableFactory<ContactCardE
|
|||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
public async create(initialInput: ContactCardEmbeddableInput, parent?: Container) {
|
||||
public create = async (initialInput: ContactCardEmbeddableInput, parent?: Container) => {
|
||||
return new ContactCardEmbeddable(
|
||||
initialInput,
|
||||
{
|
||||
|
@ -82,5 +79,5 @@ export class ContactCardEmbeddableFactory extends EmbeddableFactory<ContactCardE
|
|||
},
|
||||
parent
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import { UiActionsStart } from 'src/plugins/ui_actions/public';
|
||||
import { Container, EmbeddableFactory } from '../../..';
|
||||
import { Container, EmbeddableFactoryDefinition } from '../../..';
|
||||
import { ContactCardEmbeddable, ContactCardEmbeddableInput } from './contact_card_embeddable';
|
||||
import { CONTACT_CARD_EMBEDDABLE } from './contact_card_embeddable_factory';
|
||||
|
||||
|
@ -27,14 +27,12 @@ interface SlowContactCardEmbeddableFactoryOptions {
|
|||
loadTickCount?: number;
|
||||
}
|
||||
|
||||
export class SlowContactCardEmbeddableFactory extends EmbeddableFactory<
|
||||
ContactCardEmbeddableInput
|
||||
> {
|
||||
export class SlowContactCardEmbeddableFactory
|
||||
implements EmbeddableFactoryDefinition<ContactCardEmbeddableInput> {
|
||||
private loadTickCount = 0;
|
||||
public readonly type = CONTACT_CARD_EMBEDDABLE;
|
||||
|
||||
constructor(private readonly options: SlowContactCardEmbeddableFactoryOptions) {
|
||||
super();
|
||||
if (options.loadTickCount) {
|
||||
this.loadTickCount = options.loadTickCount;
|
||||
}
|
||||
|
@ -48,10 +46,10 @@ export class SlowContactCardEmbeddableFactory extends EmbeddableFactory<
|
|||
return 'slow to load contact card';
|
||||
}
|
||||
|
||||
public async create(initialInput: ContactCardEmbeddableInput, parent?: Container) {
|
||||
public create = async (initialInput: ContactCardEmbeddableInput, parent?: Container) => {
|
||||
for (let i = 0; i < this.loadTickCount; i++) {
|
||||
await Promise.resolve();
|
||||
}
|
||||
return new ContactCardEmbeddable(initialInput, { execAction: this.options.execAction }, parent);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -18,24 +18,21 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Container, EmbeddableFactory } from '../..';
|
||||
import { Container, EmbeddableFactoryDefinition } from '../..';
|
||||
import {
|
||||
FilterableContainer,
|
||||
FilterableContainerInput,
|
||||
FILTERABLE_CONTAINER,
|
||||
} from './filterable_container';
|
||||
import { EmbeddableFactoryOptions } from '../../embeddables/embeddable_factory';
|
||||
import { EmbeddableStart } from '../../../plugin';
|
||||
|
||||
export class FilterableContainerFactory extends EmbeddableFactory<FilterableContainerInput> {
|
||||
export class FilterableContainerFactory
|
||||
implements EmbeddableFactoryDefinition<FilterableContainerInput> {
|
||||
public readonly type = FILTERABLE_CONTAINER;
|
||||
|
||||
constructor(
|
||||
private readonly getFactory: EmbeddableStart['getEmbeddableFactory'],
|
||||
options: EmbeddableFactoryOptions<any> = {}
|
||||
) {
|
||||
super(options);
|
||||
}
|
||||
private readonly getFactory: () => Promise<EmbeddableStart['getEmbeddableFactory']>
|
||||
) {}
|
||||
|
||||
public getDisplayName() {
|
||||
return i18n.translate('embeddableApi.samples.filterableContainer.displayName', {
|
||||
|
@ -47,7 +44,8 @@ export class FilterableContainerFactory extends EmbeddableFactory<FilterableCont
|
|||
return true;
|
||||
}
|
||||
|
||||
public async create(initialInput: FilterableContainerInput, parent?: Container) {
|
||||
return new FilterableContainer(initialInput, this.getFactory, parent);
|
||||
}
|
||||
public create = async (initialInput: FilterableContainerInput, parent?: Container) => {
|
||||
const getEmbeddableFactory = await this.getFactory();
|
||||
return new FilterableContainer(initialInput, getEmbeddableFactory, parent);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -23,10 +23,11 @@ import {
|
|||
FilterableEmbeddableInput,
|
||||
FILTERABLE_EMBEDDABLE,
|
||||
} from './filterable_embeddable';
|
||||
import { EmbeddableFactory } from '../../embeddables';
|
||||
import { EmbeddableFactoryDefinition } from '../../embeddables';
|
||||
import { IContainer } from '../../containers';
|
||||
|
||||
export class FilterableEmbeddableFactory extends EmbeddableFactory<FilterableEmbeddableInput> {
|
||||
export class FilterableEmbeddableFactory
|
||||
implements EmbeddableFactoryDefinition<FilterableEmbeddableInput> {
|
||||
public readonly type = FILTERABLE_EMBEDDABLE;
|
||||
|
||||
public async isEditable() {
|
||||
|
|
|
@ -30,6 +30,7 @@ export type Start = jest.Mocked<EmbeddableStart>;
|
|||
const createSetupContract = (): Setup => {
|
||||
const setupContract: Setup = {
|
||||
registerEmbeddableFactory: jest.fn(),
|
||||
setCustomEmbeddableFactoryProvider: jest.fn(),
|
||||
};
|
||||
return setupContract;
|
||||
};
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
*/
|
||||
import { coreMock } from '../../../core/public/mocks';
|
||||
import { testPlugin } from './tests/test_plugin';
|
||||
import { EmbeddableFactoryProvider } from './types';
|
||||
import { defaultEmbeddableFactoryProvider } from './lib';
|
||||
import { HelloWorldEmbeddable } from '../../../../examples/embeddable_examples/public';
|
||||
|
||||
test('cannot register embeddable factory with the same ID', async () => {
|
||||
const coreSetup = coreMock.createSetup();
|
||||
|
@ -33,3 +36,75 @@ test('cannot register embeddable factory with the same ID', async () => {
|
|||
'Embeddable factory [embeddableFactoryId = ID] already registered in Embeddables API.'
|
||||
);
|
||||
});
|
||||
|
||||
test('can set custom embeddable factory provider', async () => {
|
||||
const coreSetup = coreMock.createSetup();
|
||||
const coreStart = coreMock.createStart();
|
||||
const { setup, doStart } = testPlugin(coreSetup, coreStart);
|
||||
|
||||
const customProvider: EmbeddableFactoryProvider = def => ({
|
||||
...defaultEmbeddableFactoryProvider(def),
|
||||
getDisplayName: () => 'Intercepted!',
|
||||
});
|
||||
|
||||
setup.setCustomEmbeddableFactoryProvider(customProvider);
|
||||
setup.registerEmbeddableFactory('test', {
|
||||
type: 'test',
|
||||
create: () => Promise.resolve(undefined),
|
||||
getDisplayName: () => 'Test',
|
||||
isEditable: () => Promise.resolve(true),
|
||||
});
|
||||
|
||||
const start = doStart();
|
||||
const factory = start.getEmbeddableFactory('test');
|
||||
expect(factory!.getDisplayName()).toEqual('Intercepted!');
|
||||
});
|
||||
|
||||
test('custom embeddable factory provider test for intercepting embeddable creation and destruction', async () => {
|
||||
const coreSetup = coreMock.createSetup();
|
||||
const coreStart = coreMock.createStart();
|
||||
const { setup, doStart } = testPlugin(coreSetup, coreStart);
|
||||
|
||||
let updateCount = 0;
|
||||
const customProvider: EmbeddableFactoryProvider = def => {
|
||||
return {
|
||||
...defaultEmbeddableFactoryProvider(def),
|
||||
create: async (input, parent) => {
|
||||
const embeddable = await defaultEmbeddableFactoryProvider(def).create(input, parent);
|
||||
if (embeddable) {
|
||||
const subscription = embeddable.getInput$().subscribe(
|
||||
() => {
|
||||
updateCount++;
|
||||
},
|
||||
() => {},
|
||||
() => {
|
||||
subscription.unsubscribe();
|
||||
updateCount = 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
return embeddable;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
setup.setCustomEmbeddableFactoryProvider(customProvider);
|
||||
setup.registerEmbeddableFactory('test', {
|
||||
type: 'test',
|
||||
create: (input, parent) => Promise.resolve(new HelloWorldEmbeddable(input, parent)),
|
||||
getDisplayName: () => 'Test',
|
||||
isEditable: () => Promise.resolve(true),
|
||||
});
|
||||
|
||||
const start = doStart();
|
||||
const factory = start.getEmbeddableFactory('test');
|
||||
|
||||
const embeddable = await factory?.create({ id: '123' });
|
||||
embeddable!.updateInput({ title: 'boo' });
|
||||
// initial subscription, plus the second update.
|
||||
expect(updateCount).toEqual(2);
|
||||
|
||||
embeddable!.destroy();
|
||||
await new Promise(resolve => process.nextTick(resolve));
|
||||
expect(updateCount).toEqual(0);
|
||||
});
|
||||
|
|
|
@ -18,9 +18,16 @@
|
|||
*/
|
||||
import { UiActionsSetup } from 'src/plugins/ui_actions/public';
|
||||
import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/public';
|
||||
import { EmbeddableFactoryRegistry } from './types';
|
||||
import { EmbeddableFactoryRegistry, EmbeddableFactoryProvider } from './types';
|
||||
import { bootstrap } from './bootstrap';
|
||||
import { EmbeddableFactory, EmbeddableInput, EmbeddableOutput } from './lib';
|
||||
import {
|
||||
EmbeddableFactory,
|
||||
EmbeddableInput,
|
||||
EmbeddableOutput,
|
||||
defaultEmbeddableFactoryProvider,
|
||||
IEmbeddable,
|
||||
} from './lib';
|
||||
import { EmbeddableFactoryDefinition } from './lib/embeddables/embeddable_factory_definition';
|
||||
|
||||
export interface EmbeddableSetupDependencies {
|
||||
uiActions: UiActionsSetup;
|
||||
|
@ -29,21 +36,29 @@ export interface EmbeddableSetupDependencies {
|
|||
export interface EmbeddableSetup {
|
||||
registerEmbeddableFactory: <I extends EmbeddableInput, O extends EmbeddableOutput>(
|
||||
id: string,
|
||||
factory: EmbeddableFactory<I, O>
|
||||
factory: EmbeddableFactoryDefinition<I, O>
|
||||
) => void;
|
||||
setCustomEmbeddableFactoryProvider: (customProvider: EmbeddableFactoryProvider) => void;
|
||||
}
|
||||
|
||||
export interface EmbeddableStart {
|
||||
getEmbeddableFactory: <
|
||||
I extends EmbeddableInput = EmbeddableInput,
|
||||
O extends EmbeddableOutput = EmbeddableOutput
|
||||
O extends EmbeddableOutput = EmbeddableOutput,
|
||||
E extends IEmbeddable<I, O> = IEmbeddable<I, O>
|
||||
>(
|
||||
embeddableFactoryId: string
|
||||
) => EmbeddableFactory<I, O> | undefined;
|
||||
) => EmbeddableFactory<I, O, E> | undefined;
|
||||
getEmbeddableFactories: () => IterableIterator<EmbeddableFactory>;
|
||||
}
|
||||
|
||||
export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, EmbeddableStart> {
|
||||
private readonly embeddableFactoryDefinitions: Map<
|
||||
string,
|
||||
EmbeddableFactoryDefinition
|
||||
> = new Map();
|
||||
private readonly embeddableFactories: EmbeddableFactoryRegistry = new Map();
|
||||
private customEmbeddableFactoryProvider?: EmbeddableFactoryProvider;
|
||||
|
||||
constructor(initializerContext: PluginInitializerContext) {}
|
||||
|
||||
|
@ -52,34 +67,57 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
|
|||
|
||||
return {
|
||||
registerEmbeddableFactory: this.registerEmbeddableFactory,
|
||||
setCustomEmbeddableFactoryProvider: (provider: EmbeddableFactoryProvider) => {
|
||||
if (this.customEmbeddableFactoryProvider) {
|
||||
throw new Error(
|
||||
'Custom embeddable factory provider is already set, and can only be set once'
|
||||
);
|
||||
}
|
||||
this.customEmbeddableFactoryProvider = provider;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public start(core: CoreStart) {
|
||||
public start(core: CoreStart): EmbeddableStart {
|
||||
this.embeddableFactoryDefinitions.forEach(def => {
|
||||
this.embeddableFactories.set(
|
||||
def.type,
|
||||
this.customEmbeddableFactoryProvider
|
||||
? this.customEmbeddableFactoryProvider(def)
|
||||
: defaultEmbeddableFactoryProvider(def)
|
||||
);
|
||||
});
|
||||
return {
|
||||
getEmbeddableFactory: this.getEmbeddableFactory,
|
||||
getEmbeddableFactories: () => this.embeddableFactories.values(),
|
||||
getEmbeddableFactories: () => {
|
||||
this.ensureFactoriesExist();
|
||||
return this.embeddableFactories.values();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public stop() {}
|
||||
|
||||
private registerEmbeddableFactory = (embeddableFactoryId: string, factory: EmbeddableFactory) => {
|
||||
if (this.embeddableFactories.has(embeddableFactoryId)) {
|
||||
private registerEmbeddableFactory = (
|
||||
embeddableFactoryId: string,
|
||||
factory: EmbeddableFactoryDefinition
|
||||
) => {
|
||||
if (this.embeddableFactoryDefinitions.has(embeddableFactoryId)) {
|
||||
throw new Error(
|
||||
`Embeddable factory [embeddableFactoryId = ${embeddableFactoryId}] already registered in Embeddables API.`
|
||||
);
|
||||
}
|
||||
|
||||
this.embeddableFactories.set(embeddableFactoryId, factory);
|
||||
this.embeddableFactoryDefinitions.set(embeddableFactoryId, factory);
|
||||
};
|
||||
|
||||
private getEmbeddableFactory = <
|
||||
I extends EmbeddableInput = EmbeddableInput,
|
||||
O extends EmbeddableOutput = EmbeddableOutput
|
||||
O extends EmbeddableOutput = EmbeddableOutput,
|
||||
E extends IEmbeddable<I, O> = IEmbeddable<I, O>
|
||||
>(
|
||||
embeddableFactoryId: string
|
||||
) => {
|
||||
): EmbeddableFactory<I, O, E> => {
|
||||
this.ensureFactoryExists(embeddableFactoryId);
|
||||
const factory = this.embeddableFactories.get(embeddableFactoryId);
|
||||
|
||||
if (!factory) {
|
||||
|
@ -88,6 +126,24 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
|
|||
);
|
||||
}
|
||||
|
||||
return factory as EmbeddableFactory<I, O>;
|
||||
return factory as EmbeddableFactory<I, O, E>;
|
||||
};
|
||||
|
||||
// These two functions are only to support legacy plugins registering factories after the start lifecycle.
|
||||
private ensureFactoriesExist() {
|
||||
this.embeddableFactoryDefinitions.forEach(def => this.ensureFactoryExists(def.type));
|
||||
}
|
||||
|
||||
private ensureFactoryExists(type: string) {
|
||||
if (!this.embeddableFactories.get(type)) {
|
||||
const def = this.embeddableFactoryDefinitions.get(type);
|
||||
if (!def) return;
|
||||
this.embeddableFactories.set(
|
||||
type,
|
||||
this.customEmbeddableFactoryProvider
|
||||
? this.customEmbeddableFactoryProvider(def)
|
||||
: defaultEmbeddableFactoryProvider(def)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,13 +36,13 @@ import { esFilters } from '../../../../plugins/data/public';
|
|||
|
||||
test('ApplyFilterAction applies the filter to the root of the container tree', async () => {
|
||||
const { doStart, setup } = testPlugin();
|
||||
const api = doStart();
|
||||
|
||||
const factory1 = new FilterableContainerFactory(api.getEmbeddableFactory);
|
||||
const factory2 = new FilterableEmbeddableFactory();
|
||||
|
||||
setup.registerEmbeddableFactory(factory1.type, factory1);
|
||||
const factory1 = new FilterableContainerFactory(async () => await api.getEmbeddableFactory);
|
||||
setup.registerEmbeddableFactory(factory2.type, factory2);
|
||||
setup.registerEmbeddableFactory(factory1.type, factory1);
|
||||
|
||||
const api = doStart();
|
||||
|
||||
const applyFilterAction = createFilterAction();
|
||||
|
||||
|
@ -63,7 +63,9 @@ test('ApplyFilterAction applies the filter to the root of the container tree', a
|
|||
FilterableContainer
|
||||
>(FILTERABLE_CONTAINER, { panels: {}, id: 'Node2' });
|
||||
|
||||
if (isErrorEmbeddable(node1) || isErrorEmbeddable(node2)) throw new Error();
|
||||
if (isErrorEmbeddable(node1) || isErrorEmbeddable(node2)) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
const embeddable = await node2.addNewEmbeddable<
|
||||
FilterableEmbeddableInput,
|
||||
|
@ -94,9 +96,11 @@ test('ApplyFilterAction applies the filter to the root of the container tree', a
|
|||
|
||||
test('ApplyFilterAction is incompatible if the root container does not accept a filter as input', async () => {
|
||||
const { doStart, coreStart, setup } = testPlugin();
|
||||
const api = doStart();
|
||||
const inspector = inspectorPluginMock.createStartContract();
|
||||
|
||||
const factory = new FilterableEmbeddableFactory();
|
||||
setup.registerEmbeddableFactory(factory.type, factory);
|
||||
const api = doStart();
|
||||
const applyFilterAction = createFilterAction();
|
||||
const parent = new HelloWorldContainer(
|
||||
{ id: 'root', panels: {} },
|
||||
|
@ -110,10 +114,6 @@ test('ApplyFilterAction is incompatible if the root container does not accept a
|
|||
SavedObjectFinder: () => null,
|
||||
}
|
||||
);
|
||||
|
||||
const factory = new FilterableEmbeddableFactory();
|
||||
setup.registerEmbeddableFactory(factory.type, factory);
|
||||
|
||||
const embeddable = await parent.addNewEmbeddable<
|
||||
FilterableContainerInput,
|
||||
EmbeddableOutput,
|
||||
|
@ -130,12 +130,12 @@ test('ApplyFilterAction is incompatible if the root container does not accept a
|
|||
|
||||
test('trying to execute on incompatible context throws an error ', async () => {
|
||||
const { doStart, coreStart, setup } = testPlugin();
|
||||
const api = doStart();
|
||||
const inspector = inspectorPluginMock.createStartContract();
|
||||
|
||||
const factory = new FilterableEmbeddableFactory();
|
||||
setup.registerEmbeddableFactory(factory.type, factory);
|
||||
|
||||
const api = doStart();
|
||||
const applyFilterAction = createFilterAction();
|
||||
const parent = new HelloWorldContainer(
|
||||
{ id: 'root', panels: {} },
|
||||
|
|
|
@ -56,8 +56,6 @@ async function creatHelloWorldContainerAndEmbeddable(
|
|||
const coreSetup = coreMock.createSetup();
|
||||
const coreStart = coreMock.createStart();
|
||||
const { setup, doStart, uiActions } = testPlugin(coreSetup, coreStart);
|
||||
const start = doStart();
|
||||
|
||||
const filterableFactory = new FilterableEmbeddableFactory();
|
||||
const slowContactCardFactory = new SlowContactCardEmbeddableFactory({
|
||||
execAction: uiActions.executeTriggerActions,
|
||||
|
@ -68,6 +66,8 @@ async function creatHelloWorldContainerAndEmbeddable(
|
|||
setup.registerEmbeddableFactory(slowContactCardFactory.type, slowContactCardFactory);
|
||||
setup.registerEmbeddableFactory(helloWorldFactory.type, helloWorldFactory);
|
||||
|
||||
const start = doStart();
|
||||
|
||||
const container = new HelloWorldContainer(containerInput, {
|
||||
getActions: uiActions.getTriggerCompatibleActions,
|
||||
getEmbeddableFactory: start.getEmbeddableFactory,
|
||||
|
@ -563,6 +563,13 @@ test('Container changes made directly after adding a new embeddable are propagat
|
|||
const coreSetup = coreMock.createSetup();
|
||||
const coreStart = coreMock.createStart();
|
||||
const { setup, doStart, uiActions } = testPlugin(coreSetup, coreStart);
|
||||
|
||||
const factory = new SlowContactCardEmbeddableFactory({
|
||||
loadTickCount: 3,
|
||||
execAction: uiActions.executeTriggerActions,
|
||||
});
|
||||
setup.registerEmbeddableFactory(factory.type, factory);
|
||||
|
||||
const start = doStart();
|
||||
|
||||
const container = new HelloWorldContainer(
|
||||
|
@ -582,12 +589,6 @@ test('Container changes made directly after adding a new embeddable are propagat
|
|||
}
|
||||
);
|
||||
|
||||
const factory = new SlowContactCardEmbeddableFactory({
|
||||
loadTickCount: 3,
|
||||
execAction: uiActions.executeTriggerActions,
|
||||
});
|
||||
setup.registerEmbeddableFactory(factory.type, factory);
|
||||
|
||||
const subscription = Rx.merge(container.getOutput$(), container.getInput$())
|
||||
.pipe(skip(2))
|
||||
.subscribe(() => {
|
||||
|
@ -759,12 +760,13 @@ test('untilEmbeddableLoaded resolves with undefined if child is subsequently rem
|
|||
coreMock.createSetup(),
|
||||
coreMock.createStart()
|
||||
);
|
||||
const start = doStart();
|
||||
const factory = new SlowContactCardEmbeddableFactory({
|
||||
loadTickCount: 3,
|
||||
execAction: uiActions.executeTriggerActions,
|
||||
});
|
||||
setup.registerEmbeddableFactory(factory.type, factory);
|
||||
|
||||
const start = doStart();
|
||||
const container = new HelloWorldContainer(
|
||||
{
|
||||
id: 'hello',
|
||||
|
@ -799,12 +801,12 @@ test('adding a panel then subsequently removing it before its loaded removes the
|
|||
coreMock.createSetup(),
|
||||
coreMock.createStart()
|
||||
);
|
||||
const start = doStart();
|
||||
const factory = new SlowContactCardEmbeddableFactory({
|
||||
loadTickCount: 1,
|
||||
execAction: uiActions.executeTriggerActions,
|
||||
});
|
||||
setup.registerEmbeddableFactory(factory.type, factory);
|
||||
const start = doStart();
|
||||
const container = new HelloWorldContainer(
|
||||
{
|
||||
id: 'hello',
|
||||
|
|
|
@ -47,15 +47,14 @@ beforeEach(async () => {
|
|||
coreMock.createSetup(),
|
||||
coreMock.createStart()
|
||||
);
|
||||
api = doStart();
|
||||
|
||||
const contactCardFactory = new ContactCardEmbeddableFactory(
|
||||
{},
|
||||
uiActions.executeTriggerActions,
|
||||
{} as any
|
||||
);
|
||||
setup.registerEmbeddableFactory(contactCardFactory.type, contactCardFactory);
|
||||
|
||||
api = doStart();
|
||||
container = new HelloWorldContainer(
|
||||
{ id: '123', panels: {} },
|
||||
{
|
||||
|
|
|
@ -41,7 +41,6 @@ const { setup, doStart, coreStart, uiActions } = testPlugin(
|
|||
coreMock.createSetup(),
|
||||
coreMock.createStart()
|
||||
);
|
||||
const start = doStart();
|
||||
|
||||
setup.registerEmbeddableFactory(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory());
|
||||
const factory = new SlowContactCardEmbeddableFactory({
|
||||
|
@ -51,6 +50,8 @@ const factory = new SlowContactCardEmbeddableFactory({
|
|||
setup.registerEmbeddableFactory(CONTACT_CARD_EMBEDDABLE, factory);
|
||||
setup.registerEmbeddableFactory(HELLO_WORLD_EMBEDDABLE, new HelloWorldEmbeddableFactory());
|
||||
|
||||
const start = doStart();
|
||||
|
||||
test('Explicit embeddable input mapped to undefined will default to inherited', async () => {
|
||||
const derivedFilter: Filter = {
|
||||
$state: { store: esFilters.FilterStateStore.APP_STATE },
|
||||
|
|
|
@ -35,16 +35,16 @@ test('returns empty list if there are no embeddable factories', () => {
|
|||
|
||||
test('returns existing embeddable factories', () => {
|
||||
const { setup, doStart } = testPlugin();
|
||||
const start = doStart();
|
||||
const { length } = [...start.getEmbeddableFactories()];
|
||||
|
||||
const factory1 = new FilterableContainerFactory(start.getEmbeddableFactory);
|
||||
const factory2 = new ContactCardEmbeddableFactory({} as any, (() => null) as any, {} as any);
|
||||
const factory1 = new FilterableContainerFactory(async () => await start.getEmbeddableFactory);
|
||||
const factory2 = new ContactCardEmbeddableFactory((() => null) as any, {} as any);
|
||||
setup.registerEmbeddableFactory(factory1.type, factory1);
|
||||
setup.registerEmbeddableFactory(factory2.type, factory2);
|
||||
|
||||
const start = doStart();
|
||||
|
||||
const list = [...start.getEmbeddableFactories()];
|
||||
expect(list.length - length).toBe(2);
|
||||
expect(list.length).toBe(2);
|
||||
expect(!!list.find(({ type }) => factory1.type === type)).toBe(true);
|
||||
expect(!!list.find(({ type }) => factory2.type === type)).toBe(true);
|
||||
});
|
||||
|
|
|
@ -17,6 +17,22 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { EmbeddableFactory } from './lib/embeddables';
|
||||
import { SavedObjectAttributes } from 'kibana/public';
|
||||
import {
|
||||
EmbeddableFactory,
|
||||
EmbeddableInput,
|
||||
EmbeddableOutput,
|
||||
IEmbeddable,
|
||||
EmbeddableFactoryDefinition,
|
||||
} from './lib/embeddables';
|
||||
|
||||
export type EmbeddableFactoryRegistry = Map<string, EmbeddableFactory>;
|
||||
|
||||
export type EmbeddableFactoryProvider = <
|
||||
I extends EmbeddableInput = EmbeddableInput,
|
||||
O extends EmbeddableOutput = EmbeddableOutput,
|
||||
E extends IEmbeddable<I, O> = IEmbeddable<I, O>,
|
||||
T extends SavedObjectAttributes = SavedObjectAttributes
|
||||
>(
|
||||
def: EmbeddableFactoryDefinition<I, O, E, T>
|
||||
) => EmbeddableFactory<I, O, E, T>;
|
||||
|
|
|
@ -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 { Vis } from '../types';
|
||||
import { VisualizeInput, VisualizeEmbeddable } from './visualize_embeddable';
|
||||
import { IContainer, ErrorEmbeddable } from '../../../../plugins/embeddable/public';
|
||||
import { DisabledLabEmbeddable } from './disabled_lab_embeddable';
|
||||
import {
|
||||
getSavedVisualizationsLoader,
|
||||
getUISettings,
|
||||
getHttp,
|
||||
getTimeFilter,
|
||||
getCapabilities,
|
||||
} from '../services';
|
||||
|
||||
export const createVisEmbeddableFromObject = async (
|
||||
vis: Vis,
|
||||
input: Partial<VisualizeInput> & { id: string },
|
||||
parent?: IContainer
|
||||
): Promise<VisualizeEmbeddable | ErrorEmbeddable | DisabledLabEmbeddable> => {
|
||||
const savedVisualizations = getSavedVisualizationsLoader();
|
||||
|
||||
try {
|
||||
const visId = vis.id as string;
|
||||
|
||||
const editUrl = visId
|
||||
? getHttp().basePath.prepend(`/app/kibana${savedVisualizations.urlFor(visId)}`)
|
||||
: '';
|
||||
const isLabsEnabled = getUISettings().get<boolean>('visualize:enableLabs');
|
||||
|
||||
if (!isLabsEnabled && vis.type.stage === 'experimental') {
|
||||
return new DisabledLabEmbeddable(vis.title, input);
|
||||
}
|
||||
|
||||
const indexPattern = vis.data.indexPattern;
|
||||
const indexPatterns = indexPattern ? [indexPattern] : [];
|
||||
const editable = getCapabilities().visualize.save as boolean;
|
||||
return new VisualizeEmbeddable(
|
||||
getTimeFilter(),
|
||||
{
|
||||
vis,
|
||||
indexPatterns,
|
||||
editUrl,
|
||||
editable,
|
||||
},
|
||||
input,
|
||||
parent
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e); // eslint-disable-line no-console
|
||||
return new ErrorEmbeddable(e, input, parent);
|
||||
}
|
||||
};
|
|
@ -21,3 +21,4 @@ export { VisualizeEmbeddable, VisualizeInput } from './visualize_embeddable';
|
|||
export { VisualizeEmbeddableFactory } from './visualize_embeddable_factory';
|
||||
export { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
|
||||
export { VIS_EVENT_TO_TRIGGER } from './events';
|
||||
export { createVisEmbeddableFromObject } from './create_vis_embeddable_from_object';
|
||||
|
|
|
@ -33,8 +33,8 @@ import {
|
|||
EmbeddableInput,
|
||||
EmbeddableOutput,
|
||||
Embeddable,
|
||||
Container,
|
||||
EmbeddableVisTriggerContext,
|
||||
IContainer,
|
||||
} from '../../../../plugins/embeddable/public';
|
||||
import { dispatchRenderComplete } from '../../../../plugins/kibana_utils/public';
|
||||
import { IExpressionLoaderParams, ExpressionsStart } from '../../../../plugins/expressions/public';
|
||||
|
@ -89,7 +89,7 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
|
|||
timefilter: TimefilterContract,
|
||||
{ vis, editUrl, indexPatterns, editable }: VisualizeEmbeddableConfiguration,
|
||||
initialInput: VisualizeInput,
|
||||
parent?: Container
|
||||
parent?: IContainer
|
||||
) {
|
||||
super(
|
||||
initialInput,
|
||||
|
@ -116,7 +116,6 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
|
|||
})
|
||||
);
|
||||
}
|
||||
|
||||
public getVisualizationDescription() {
|
||||
return this.vis.description;
|
||||
}
|
||||
|
|
|
@ -18,70 +18,68 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { SavedObjectMetaData } from 'src/plugins/saved_objects/public';
|
||||
import { SavedObjectAttributes } from '../../../../core/public';
|
||||
import {
|
||||
Container,
|
||||
EmbeddableFactory,
|
||||
EmbeddableFactoryDefinition,
|
||||
EmbeddableOutput,
|
||||
ErrorEmbeddable,
|
||||
IContainer,
|
||||
} from '../../../../plugins/embeddable/public';
|
||||
import { DisabledLabEmbeddable } from './disabled_lab_embeddable';
|
||||
import { VisualizeEmbeddable, VisualizeInput, VisualizeOutput } from './visualize_embeddable';
|
||||
import { Vis } from '../types';
|
||||
import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
|
||||
import { Vis } from '../vis';
|
||||
import {
|
||||
getCapabilities,
|
||||
getHttp,
|
||||
getTypes,
|
||||
getUISettings,
|
||||
getSavedVisualizationsLoader,
|
||||
getTimeFilter,
|
||||
} from '../services';
|
||||
import { showNewVisModal } from '../wizard';
|
||||
import { convertToSerializedVis } from '../saved_visualizations/_saved_vis';
|
||||
import { createVisEmbeddableFromObject } from './create_vis_embeddable_from_object';
|
||||
|
||||
interface VisualizationAttributes extends SavedObjectAttributes {
|
||||
visState: string;
|
||||
}
|
||||
|
||||
export class VisualizeEmbeddableFactory extends EmbeddableFactory<
|
||||
VisualizeInput,
|
||||
VisualizeOutput | EmbeddableOutput,
|
||||
VisualizeEmbeddable | DisabledLabEmbeddable,
|
||||
VisualizationAttributes
|
||||
> {
|
||||
export class VisualizeEmbeddableFactory
|
||||
implements
|
||||
EmbeddableFactoryDefinition<
|
||||
VisualizeInput,
|
||||
VisualizeOutput | EmbeddableOutput,
|
||||
VisualizeEmbeddable | DisabledLabEmbeddable,
|
||||
VisualizationAttributes
|
||||
> {
|
||||
public readonly type = VISUALIZE_EMBEDDABLE_TYPE;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
savedObjectMetaData: {
|
||||
name: i18n.translate('visualizations.savedObjectName', { defaultMessage: 'Visualization' }),
|
||||
includeFields: ['visState'],
|
||||
type: 'visualization',
|
||||
getIconForSavedObject: savedObject => {
|
||||
return (
|
||||
getTypes().get(JSON.parse(savedObject.attributes.visState).type).icon || 'visualizeApp'
|
||||
);
|
||||
},
|
||||
getTooltipForSavedObject: savedObject => {
|
||||
return `${savedObject.attributes.title} (${
|
||||
getTypes().get(JSON.parse(savedObject.attributes.visState).type).title
|
||||
})`;
|
||||
},
|
||||
showSavedObject: savedObject => {
|
||||
const typeName: string = JSON.parse(savedObject.attributes.visState).type;
|
||||
const visType = getTypes().get(typeName);
|
||||
if (!visType) {
|
||||
return false;
|
||||
}
|
||||
if (getUISettings().get('visualize:enableLabs')) {
|
||||
return true;
|
||||
}
|
||||
return visType.stage !== 'experimental';
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
public readonly savedObjectMetaData: SavedObjectMetaData<VisualizationAttributes> = {
|
||||
name: i18n.translate('visualizations.savedObjectName', { defaultMessage: 'Visualization' }),
|
||||
includeFields: ['visState'],
|
||||
type: 'visualization',
|
||||
getIconForSavedObject: savedObject => {
|
||||
return (
|
||||
getTypes().get(JSON.parse(savedObject.attributes.visState).type).icon || 'visualizeApp'
|
||||
);
|
||||
},
|
||||
getTooltipForSavedObject: savedObject => {
|
||||
return `${savedObject.attributes.title} (${
|
||||
getTypes().get(JSON.parse(savedObject.attributes.visState).type).title
|
||||
})`;
|
||||
},
|
||||
showSavedObject: savedObject => {
|
||||
const typeName: string = JSON.parse(savedObject.attributes.visState).type;
|
||||
const visType = getTypes().get(typeName);
|
||||
if (!visType) {
|
||||
return false;
|
||||
}
|
||||
if (getUISettings().get('visualize:enableLabs')) {
|
||||
return true;
|
||||
}
|
||||
return visType.stage !== 'experimental';
|
||||
},
|
||||
};
|
||||
constructor() {}
|
||||
|
||||
public async isEditable() {
|
||||
return getCapabilities().visualize.save as boolean;
|
||||
|
@ -93,56 +91,17 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
|
|||
});
|
||||
}
|
||||
|
||||
public async createFromObject(
|
||||
vis: Vis,
|
||||
input: Partial<VisualizeInput> & { id: string },
|
||||
parent?: Container
|
||||
): Promise<VisualizeEmbeddable | ErrorEmbeddable | DisabledLabEmbeddable> {
|
||||
const savedVisualizations = getSavedVisualizationsLoader();
|
||||
|
||||
try {
|
||||
const visId = vis.id as string;
|
||||
|
||||
const editUrl = visId
|
||||
? getHttp().basePath.prepend(`/app/kibana${savedVisualizations.urlFor(visId)}`)
|
||||
: '';
|
||||
const isLabsEnabled = getUISettings().get<boolean>('visualize:enableLabs');
|
||||
|
||||
if (!isLabsEnabled && vis.type.stage === 'experimental') {
|
||||
return new DisabledLabEmbeddable(vis.title, input);
|
||||
}
|
||||
|
||||
const indexPattern = vis.data.indexPattern;
|
||||
const indexPatterns = indexPattern ? [indexPattern] : [];
|
||||
const editable = await this.isEditable();
|
||||
return new VisualizeEmbeddable(
|
||||
getTimeFilter(),
|
||||
{
|
||||
vis,
|
||||
indexPatterns,
|
||||
editUrl,
|
||||
editable,
|
||||
},
|
||||
input,
|
||||
parent
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e); // eslint-disable-line no-console
|
||||
return new ErrorEmbeddable(e, input, parent);
|
||||
}
|
||||
}
|
||||
|
||||
public async createFromSavedObject(
|
||||
savedObjectId: string,
|
||||
input: Partial<VisualizeInput> & { id: string },
|
||||
parent?: Container
|
||||
parent?: IContainer
|
||||
): Promise<VisualizeEmbeddable | ErrorEmbeddable | DisabledLabEmbeddable> {
|
||||
const savedVisualizations = getSavedVisualizationsLoader();
|
||||
|
||||
try {
|
||||
const savedObject = await savedVisualizations.get(savedObjectId);
|
||||
const vis = new Vis(savedObject.visState.type, await convertToSerializedVis(savedObject));
|
||||
return this.createFromObject(vis, input, parent);
|
||||
return createVisEmbeddableFromObject(vis, input, parent);
|
||||
} catch (e) {
|
||||
console.error(e); // eslint-disable-line no-console
|
||||
return new ErrorEmbeddable(e, input, parent);
|
||||
|
|
|
@ -43,6 +43,9 @@ const createStartContract = (): VisualizationsStart => ({
|
|||
createVis: jest.fn(),
|
||||
convertFromSerializedVis: jest.fn(),
|
||||
convertToSerializedVis: jest.fn(),
|
||||
__LEGACY: {
|
||||
createVisEmbeddableFromObject: jest.fn(),
|
||||
},
|
||||
});
|
||||
|
||||
const createInstance = async () => {
|
||||
|
|
|
@ -37,7 +37,11 @@ import {
|
|||
setChrome,
|
||||
setOverlays,
|
||||
} from './services';
|
||||
import { VISUALIZE_EMBEDDABLE_TYPE, VisualizeEmbeddableFactory } from './embeddable';
|
||||
import {
|
||||
VISUALIZE_EMBEDDABLE_TYPE,
|
||||
VisualizeEmbeddableFactory,
|
||||
createVisEmbeddableFromObject,
|
||||
} from './embeddable';
|
||||
import { ExpressionsSetup, ExpressionsStart } from '../../../plugins/expressions/public';
|
||||
import { EmbeddableSetup } from '../../../plugins/embeddable/public';
|
||||
import { visualization as visualizationFunction } from './expressions/visualization_function';
|
||||
|
@ -69,6 +73,7 @@ export interface VisualizationsStart extends TypesStart {
|
|||
convertToSerializedVis: typeof convertToSerializedVis;
|
||||
convertFromSerializedVis: typeof convertFromSerializedVis;
|
||||
showNewVisModal: typeof showNewVisModal;
|
||||
__LEGACY: { createVisEmbeddableFromObject: typeof createVisEmbeddableFromObject };
|
||||
}
|
||||
|
||||
export interface VisualizationsSetupDeps {
|
||||
|
@ -163,6 +168,7 @@ export class VisualizationsPlugin
|
|||
convertToSerializedVis,
|
||||
convertFromSerializedVis,
|
||||
savedVisualizationsLoader,
|
||||
__LEGACY: { createVisEmbeddableFromObject },
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ import {
|
|||
import {
|
||||
DASHBOARD_CONTAINER_TYPE,
|
||||
DashboardContainer,
|
||||
DashboardContainerFactory,
|
||||
DashboardContainerInput,
|
||||
} from '../../../../../../../../src/plugins/dashboard/public';
|
||||
|
||||
|
@ -70,8 +69,9 @@ export class DashboardContainerExample extends React.Component<Props, State> {
|
|||
this.mounted = true;
|
||||
const dashboardFactory = this.props.getEmbeddableFactory<
|
||||
DashboardContainerInput,
|
||||
ContainerOutput
|
||||
>(DASHBOARD_CONTAINER_TYPE) as DashboardContainerFactory;
|
||||
ContainerOutput,
|
||||
DashboardContainer
|
||||
>(DASHBOARD_CONTAINER_TYPE);
|
||||
if (dashboardFactory) {
|
||||
this.container = await dashboardFactory.create(dashboardInput);
|
||||
if (this.mounted) {
|
||||
|
|
|
@ -18,7 +18,7 @@ import {
|
|||
} from '../../../../../../../src/plugins/data/public';
|
||||
import { ReactExpressionRendererType } from '../../../../../../../src/plugins/expressions/public';
|
||||
import {
|
||||
EmbeddableFactory as AbstractEmbeddableFactory,
|
||||
EmbeddableFactoryDefinition,
|
||||
ErrorEmbeddable,
|
||||
EmbeddableInput,
|
||||
IContainer,
|
||||
|
@ -36,25 +36,22 @@ interface StartServices {
|
|||
indexPatternService: IndexPatternsContract;
|
||||
}
|
||||
|
||||
export class EmbeddableFactory extends AbstractEmbeddableFactory {
|
||||
export class EmbeddableFactory implements EmbeddableFactoryDefinition {
|
||||
type = DOC_TYPE;
|
||||
savedObjectMetaData = {
|
||||
name: i18n.translate('xpack.lens.lensSavedObjectLabel', {
|
||||
defaultMessage: 'Lens Visualization',
|
||||
}),
|
||||
type: DOC_TYPE,
|
||||
getIconForSavedObject: () => 'lensApp',
|
||||
};
|
||||
|
||||
constructor(private getStartServices: () => Promise<StartServices>) {
|
||||
super({
|
||||
savedObjectMetaData: {
|
||||
name: i18n.translate('xpack.lens.lensSavedObjectLabel', {
|
||||
defaultMessage: 'Lens Visualization',
|
||||
}),
|
||||
type: DOC_TYPE,
|
||||
getIconForSavedObject: () => 'lensApp',
|
||||
},
|
||||
});
|
||||
}
|
||||
constructor(private getStartServices: () => Promise<StartServices>) {}
|
||||
|
||||
public async isEditable() {
|
||||
public isEditable = async () => {
|
||||
const { capabilities } = await this.getStartServices();
|
||||
return capabilities.visualize.save as boolean;
|
||||
}
|
||||
};
|
||||
|
||||
canCreateNew() {
|
||||
return false;
|
||||
|
@ -66,11 +63,11 @@ export class EmbeddableFactory extends AbstractEmbeddableFactory {
|
|||
});
|
||||
}
|
||||
|
||||
async createFromSavedObject(
|
||||
createFromSavedObject = async (
|
||||
savedObjectId: string,
|
||||
input: Partial<EmbeddableInput> & { id: string },
|
||||
parent?: IContainer
|
||||
) {
|
||||
) => {
|
||||
const {
|
||||
savedObjectsClient,
|
||||
coreHttp,
|
||||
|
@ -111,7 +108,7 @@ export class EmbeddableFactory extends AbstractEmbeddableFactory {
|
|||
input,
|
||||
parent
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
async create(input: EmbeddableInput) {
|
||||
return new ErrorEmbeddable('Lens can only be created from a saved object', input);
|
||||
|
|
|
@ -30,17 +30,15 @@
|
|||
### Creating a Map embeddable from state
|
||||
```
|
||||
const factory = new MapEmbeddableFactory();
|
||||
const state = {
|
||||
layerList: [], // where layerList is same as saved object layerListJSON property (unstringified)
|
||||
title: 'my map',
|
||||
}
|
||||
const input = {
|
||||
hideFilterActions: true,
|
||||
isLayerTOCOpen: false,
|
||||
openTOCDetails: ['tfi3f', 'edh66'],
|
||||
mapCenter: { lat: 0.0, lon: 0.0, zoom: 7 }
|
||||
}
|
||||
const mapEmbeddable = await factory.createFromState(state, input, parent);
|
||||
const mapEmbeddable = await factory.create(input, parent);
|
||||
// where layerList is same as saved object layerListJSON property (unstringified))
|
||||
mapEmbeddable.setLayerList([]);
|
||||
```
|
||||
|
||||
#### Customize tooltip
|
||||
|
@ -62,7 +60,9 @@ const renderTooltipContent = ({ addFilters, closeTooltip, features, isLocked, lo
|
|||
return <div>Custom tooltip content</div>;
|
||||
}
|
||||
|
||||
const mapEmbeddable = await factory.createFromState(state, input, parent, renderTooltipContent);
|
||||
const mapEmbeddable = await factory.create(input, parent)
|
||||
mapEmbeddable.setLayerList(layerList);
|
||||
mapEmbeddable.setRenderTooltipContent(renderTooltipContent);
|
||||
```
|
||||
|
||||
|
||||
|
@ -80,7 +80,10 @@ const eventHandlers = {
|
|||
},
|
||||
}
|
||||
|
||||
const mapEmbeddable = await factory.createFromState(state, input, parent, renderTooltipContent, eventHandlers);
|
||||
const mapEmbeddable = await factory.create(input, parent);
|
||||
mapEmbeddable.setLayerList(layerList);
|
||||
mapEmbeddable.setRenderTooltipContent(renderTooltipContent);
|
||||
mapEmbeddable.setEventHandlers(eventHandlers);
|
||||
```
|
||||
|
||||
|
||||
|
@ -90,55 +93,13 @@ Geojson sources will not update unless you modify `__featureCollection` property
|
|||
|
||||
```
|
||||
const factory = new MapEmbeddableFactory();
|
||||
const state = {
|
||||
layerList: [
|
||||
{
|
||||
'id': 'gaxya',
|
||||
'label': 'My geospatial data',
|
||||
'minZoom': 0,
|
||||
'maxZoom': 24,
|
||||
'alpha': 1,
|
||||
'sourceDescriptor': {
|
||||
'id': 'b7486',
|
||||
'type': 'GEOJSON_FILE',
|
||||
'__featureCollection': {
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[0, 0], [10, 10], [10, 0], [0, 0]
|
||||
]
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"name": "null island",
|
||||
"another_prop": "something else interesting"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
'visible': true,
|
||||
'style': {
|
||||
'type': 'VECTOR',
|
||||
'properties': {}
|
||||
},
|
||||
'type': 'VECTOR'
|
||||
}
|
||||
],
|
||||
title: 'my map',
|
||||
}
|
||||
const input = {
|
||||
hideFilterActions: true,
|
||||
isLayerTOCOpen: false,
|
||||
openTOCDetails: ['tfi3f', 'edh66'],
|
||||
mapCenter: { lat: 0.0, lon: 0.0, zoom: 7 }
|
||||
}
|
||||
const mapEmbeddable = await factory.createFromState(state, input, parent);
|
||||
const mapEmbeddable = await factory.create(input, parent);
|
||||
|
||||
mapEmbeddable.setLayerList([
|
||||
{
|
||||
|
|
|
@ -129,6 +129,14 @@ export class MapEmbeddable extends Embeddable<MapEmbeddableInput, MapEmbeddableO
|
|||
this._subscription = this.getInput$().subscribe(input => this.onContainerStateChanged(input));
|
||||
}
|
||||
|
||||
setRenderTooltipContent = (renderTooltipContent: RenderToolTipContent) => {
|
||||
this._renderTooltipContent = renderTooltipContent;
|
||||
};
|
||||
|
||||
setEventHandlers = (eventHandlers: EventHandlers) => {
|
||||
this._eventHandlers = eventHandlers;
|
||||
};
|
||||
|
||||
getInspectorAdapters() {
|
||||
return getInspectorAdapters(this._store.getState());
|
||||
}
|
||||
|
|
|
@ -14,8 +14,7 @@ import { IIndexPattern } from 'src/plugins/data/public';
|
|||
import { MapEmbeddable, MapEmbeddableInput } from './map_embeddable';
|
||||
import { getIndexPatternService } from '../kibana_services';
|
||||
import {
|
||||
EmbeddableFactory,
|
||||
ErrorEmbeddable,
|
||||
EmbeddableFactoryDefinition,
|
||||
IContainer,
|
||||
} from '../../../../../../src/plugins/embeddable/public';
|
||||
|
||||
|
@ -28,25 +27,17 @@ import { getInitialLayers } from '../angular/get_initial_layers';
|
|||
import { mergeInputWithSavedMap } from './merge_input_with_saved_map';
|
||||
import '../angular/services/gis_map_saved_object_loader';
|
||||
import { bindSetupCoreAndPlugins, bindStartCoreAndPlugins } from '../plugin';
|
||||
import { RenderToolTipContent } from '../layers/tooltips/tooltip_property';
|
||||
import {
|
||||
EventHandlers,
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
} from '../../../../../plugins/maps/public/reducers/non_serializable_instances';
|
||||
|
||||
export class MapEmbeddableFactory extends EmbeddableFactory {
|
||||
export class MapEmbeddableFactory implements EmbeddableFactoryDefinition {
|
||||
type = MAP_SAVED_OBJECT_TYPE;
|
||||
|
||||
savedObjectMetaData = {
|
||||
name: i18n.translate('xpack.maps.mapSavedObjectLabel', {
|
||||
defaultMessage: 'Map',
|
||||
}),
|
||||
type: MAP_SAVED_OBJECT_TYPE,
|
||||
getIconForSavedObject: () => APP_ICON,
|
||||
};
|
||||
constructor() {
|
||||
super({
|
||||
savedObjectMetaData: {
|
||||
name: i18n.translate('xpack.maps.mapSavedObjectLabel', {
|
||||
defaultMessage: 'Map',
|
||||
}),
|
||||
type: MAP_SAVED_OBJECT_TYPE,
|
||||
getIconForSavedObject: () => APP_ICON,
|
||||
},
|
||||
});
|
||||
// Init required services. Necessary while in legacy
|
||||
bindSetupCoreAndPlugins(npSetup.core, npSetup.plugins);
|
||||
bindStartCoreAndPlugins(npStart.core, npStart.plugins);
|
||||
|
@ -103,11 +94,11 @@ export class MapEmbeddableFactory extends EmbeddableFactory {
|
|||
return await savedObjectLoader.get(savedObjectId);
|
||||
}
|
||||
|
||||
async createFromSavedObject(
|
||||
createFromSavedObject = async (
|
||||
savedObjectId: string,
|
||||
input: MapEmbeddableInput,
|
||||
parent?: IContainer
|
||||
) {
|
||||
) => {
|
||||
const savedMap = await this._fetchSavedMap(savedObjectId);
|
||||
const layerList = getInitialLayers(savedMap.layerListJSON);
|
||||
const indexPatterns = await this._getIndexPatterns(layerList);
|
||||
|
@ -135,39 +126,23 @@ export class MapEmbeddableFactory extends EmbeddableFactory {
|
|||
}
|
||||
|
||||
return embeddable;
|
||||
}
|
||||
};
|
||||
|
||||
async createFromState(
|
||||
state: { title?: string; layerList?: unknown[] },
|
||||
input: MapEmbeddableInput,
|
||||
parent: IContainer,
|
||||
renderTooltipContent: RenderToolTipContent,
|
||||
eventHandlers: EventHandlers
|
||||
) {
|
||||
const layerList = state && state.layerList ? state.layerList : getInitialLayers();
|
||||
create = async (input: MapEmbeddableInput, parent?: IContainer) => {
|
||||
const layerList = getInitialLayers();
|
||||
const indexPatterns = await this._getIndexPatterns(layerList);
|
||||
|
||||
return new MapEmbeddable(
|
||||
{
|
||||
layerList,
|
||||
title: state && state.title ? state.title : '',
|
||||
title: input.title ?? '',
|
||||
indexPatterns,
|
||||
editable: false,
|
||||
},
|
||||
input,
|
||||
parent,
|
||||
renderTooltipContent,
|
||||
eventHandlers
|
||||
parent
|
||||
);
|
||||
}
|
||||
|
||||
async create(input: MapEmbeddableInput) {
|
||||
window.location.href = chrome.addBasePath(createMapPath(''));
|
||||
return new ErrorEmbeddable(
|
||||
'Maps can only be created with createFromSavedObject or createFromState',
|
||||
input
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
npSetup.plugins.embeddable.registerEmbeddableFactory(
|
||||
|
|
|
@ -10,7 +10,10 @@ import { createPortalNode, InPortal } from 'react-reverse-portal';
|
|||
import styled, { css } from 'styled-components';
|
||||
import { npStart } from 'ui/new_platform';
|
||||
|
||||
import { EmbeddablePanel } from '../../../../../../../src/plugins/embeddable/public';
|
||||
import {
|
||||
EmbeddablePanel,
|
||||
ErrorEmbeddable,
|
||||
} from '../../../../../../../src/plugins/embeddable/public';
|
||||
import { DEFAULT_INDEX_KEY } from '../../../common/constants';
|
||||
import { getIndexPatternTitleIdMapping } from '../../hooks/api/helpers';
|
||||
import { useIndexPatterns } from '../../hooks/use_index_patterns';
|
||||
|
@ -84,7 +87,9 @@ export const EmbeddedMapComponent = ({
|
|||
setQuery,
|
||||
startDate,
|
||||
}: EmbeddedMapProps) => {
|
||||
const [embeddable, setEmbeddable] = React.useState<MapEmbeddable | null>(null);
|
||||
const [embeddable, setEmbeddable] = React.useState<MapEmbeddable | undefined | ErrorEmbeddable>(
|
||||
undefined
|
||||
);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isError, setIsError] = useState(false);
|
||||
const [isIndexError, setIsIndexError] = useState(false);
|
||||
|
|
|
@ -20,8 +20,10 @@ jest.mock('ui/new_platform');
|
|||
|
||||
const { npStart } = createUiNewPlatformMock();
|
||||
npStart.plugins.embeddable.getEmbeddableFactory = jest.fn().mockImplementation(() => ({
|
||||
createFromState: () => ({
|
||||
create: () => ({
|
||||
reload: jest.fn(),
|
||||
setRenderTooltipContent: jest.fn(),
|
||||
setLayerList: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
|
|
|
@ -11,10 +11,20 @@ import minimatch from 'minimatch';
|
|||
import { IndexPatternMapping, SetQuery } from './types';
|
||||
import { getLayerList } from './map_config';
|
||||
import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../plugins/maps/public';
|
||||
import { MapEmbeddable, RenderTooltipContentParams } from '../../../../maps/public';
|
||||
import {
|
||||
MapEmbeddable,
|
||||
RenderTooltipContentParams,
|
||||
MapEmbeddableInput,
|
||||
} from '../../../../maps/public';
|
||||
import * as i18n from './translations';
|
||||
import { Query, Filter } from '../../../../../../../src/plugins/data/public';
|
||||
import { EmbeddableStart, ViewMode } from '../../../../../../../src/plugins/embeddable/public';
|
||||
import {
|
||||
EmbeddableStart,
|
||||
isErrorEmbeddable,
|
||||
EmbeddableOutput,
|
||||
ViewMode,
|
||||
ErrorEmbeddable,
|
||||
} from '../../../../../../../src/plugins/embeddable/public';
|
||||
import { IndexPatternSavedObject } from '../../hooks/types';
|
||||
|
||||
/**
|
||||
|
@ -40,14 +50,19 @@ export const createEmbeddable = async (
|
|||
setQuery: SetQuery,
|
||||
portalNode: PortalNode,
|
||||
embeddableApi: EmbeddableStart
|
||||
): Promise<MapEmbeddable> => {
|
||||
const factory = embeddableApi.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE);
|
||||
): Promise<MapEmbeddable | ErrorEmbeddable> => {
|
||||
const factory = embeddableApi.getEmbeddableFactory<
|
||||
MapEmbeddableInput,
|
||||
EmbeddableOutput,
|
||||
MapEmbeddable
|
||||
>(MAP_SAVED_OBJECT_TYPE);
|
||||
|
||||
const state = {
|
||||
layerList: getLayerList(indexPatterns),
|
||||
if (!factory) {
|
||||
throw new Error('Map embeddable factory undefined');
|
||||
}
|
||||
|
||||
const input: MapEmbeddableInput = {
|
||||
title: i18n.MAP_TITLE,
|
||||
};
|
||||
const input = {
|
||||
id: uuid.v4(),
|
||||
filters,
|
||||
hidePanelTitles: true,
|
||||
|
@ -86,13 +101,16 @@ export const createEmbeddable = async (
|
|||
return <OutPortal node={portalNode} {...props} />;
|
||||
};
|
||||
|
||||
// @ts-ignore method added in https://github.com/elastic/kibana/pull/43878
|
||||
const embeddableObject = await factory.createFromState(
|
||||
state,
|
||||
input,
|
||||
undefined,
|
||||
renderTooltipContent
|
||||
);
|
||||
const embeddableObject = await factory.create(input);
|
||||
|
||||
if (!embeddableObject) {
|
||||
throw new Error('Map embeddable is undefined');
|
||||
}
|
||||
|
||||
if (!isErrorEmbeddable(embeddableObject)) {
|
||||
embeddableObject.setRenderTooltipContent(renderTooltipContent);
|
||||
embeddableObject.setLayerList(getLayerList(indexPatterns));
|
||||
}
|
||||
|
||||
// Wire up to app refresh action
|
||||
setQuery({
|
||||
|
|
|
@ -9,7 +9,12 @@ import uuid from 'uuid';
|
|||
import styled from 'styled-components';
|
||||
import { npStart } from 'ui/new_platform';
|
||||
|
||||
import { ViewMode } from '../../../../../../../../../src/plugins/embeddable/public';
|
||||
import {
|
||||
ViewMode,
|
||||
EmbeddableOutput,
|
||||
ErrorEmbeddable,
|
||||
isErrorEmbeddable,
|
||||
} from '../../../../../../../../../src/plugins/embeddable/public';
|
||||
import * as i18n from './translations';
|
||||
import { MapEmbeddable, MapEmbeddableInput } from '../../../../../../maps/public';
|
||||
import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../../../plugins/maps/public';
|
||||
|
@ -45,9 +50,13 @@ const EmbeddedPanel = styled.div`
|
|||
|
||||
export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProps) => {
|
||||
const { colors } = useContext(UptimeThemeContext);
|
||||
const [embeddable, setEmbeddable] = useState<MapEmbeddable>();
|
||||
const [embeddable, setEmbeddable] = useState<MapEmbeddable | ErrorEmbeddable | undefined>();
|
||||
const embeddableRoot: React.RefObject<HTMLDivElement> = useRef<HTMLDivElement>(null);
|
||||
const factory = npStart.plugins.embeddable.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE);
|
||||
const factory = npStart.plugins.embeddable.getEmbeddableFactory<
|
||||
MapEmbeddableInput,
|
||||
EmbeddableOutput,
|
||||
MapEmbeddable
|
||||
>(MAP_SAVED_OBJECT_TYPE);
|
||||
|
||||
const input: MapEmbeddableInput = {
|
||||
id: uuid.v4(),
|
||||
|
@ -76,12 +85,17 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp
|
|||
|
||||
useEffect(() => {
|
||||
async function setupEmbeddable() {
|
||||
const mapState = {
|
||||
layerList: getLayerList(upPoints, downPoints, colors),
|
||||
if (!factory) {
|
||||
throw new Error('Map embeddable not found.');
|
||||
}
|
||||
const embeddableObject = await factory.create({
|
||||
...input,
|
||||
title: i18n.MAP_TITLE,
|
||||
};
|
||||
// @ts-ignore
|
||||
const embeddableObject = await factory.createFromState(mapState, input, undefined);
|
||||
});
|
||||
|
||||
if (embeddableObject && !isErrorEmbeddable(embeddableObject)) {
|
||||
embeddableObject.setLayerList(getLayerList(upPoints, downPoints, colors));
|
||||
}
|
||||
|
||||
setEmbeddable(embeddableObject);
|
||||
}
|
||||
|
@ -93,7 +107,7 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp
|
|||
|
||||
// update map layers based on points
|
||||
useEffect(() => {
|
||||
if (embeddable) {
|
||||
if (embeddable && !isErrorEmbeddable(embeddable)) {
|
||||
embeddable.setLayerList(getLayerList(upPoints, downPoints, colors));
|
||||
}
|
||||
}, [upPoints, downPoints, embeddable, colors]);
|
||||
|
|
|
@ -10,9 +10,7 @@ import { skip } from 'rxjs/operators';
|
|||
import * as Rx from 'rxjs';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import { EmbeddableFactory } from '../../../../src/plugins/embeddable/public';
|
||||
import { TimeRangeEmbeddable, TimeRangeContainer, TIME_RANGE_EMBEDDABLE } from './test_helpers';
|
||||
import { TimeRangeEmbeddableFactory } from './test_helpers/time_range_embeddable_factory';
|
||||
import { CustomTimeRangeAction } from './custom_time_range_action';
|
||||
/* eslint-disable */
|
||||
import {
|
||||
|
@ -21,7 +19,6 @@ import {
|
|||
/* eslint-enable */
|
||||
|
||||
import {
|
||||
HelloWorldEmbeddableFactory,
|
||||
HelloWorldEmbeddable,
|
||||
HELLO_WORLD_EMBEDDABLE,
|
||||
} from '../../../../examples/embeddable_examples/public';
|
||||
|
@ -38,9 +35,6 @@ const createOpenModalMock = () => {
|
|||
};
|
||||
|
||||
test('Custom time range action prevents embeddable from using container time', async done => {
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(TIME_RANGE_EMBEDDABLE, new TimeRangeEmbeddableFactory());
|
||||
|
||||
const container = new TimeRangeContainer(
|
||||
{
|
||||
timeRange: { from: 'now-15m', to: 'now' },
|
||||
|
@ -105,9 +99,6 @@ test('Custom time range action prevents embeddable from using container time', a
|
|||
});
|
||||
|
||||
test('Removing custom time range action resets embeddable back to container time', async done => {
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(TIME_RANGE_EMBEDDABLE, new TimeRangeEmbeddableFactory());
|
||||
|
||||
const container = new TimeRangeContainer(
|
||||
{
|
||||
timeRange: { from: 'now-15m', to: 'now' },
|
||||
|
@ -182,9 +173,6 @@ test('Removing custom time range action resets embeddable back to container time
|
|||
});
|
||||
|
||||
test('Cancelling custom time range action leaves state alone', async done => {
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(TIME_RANGE_EMBEDDABLE, new TimeRangeEmbeddableFactory());
|
||||
|
||||
const container = new TimeRangeContainer(
|
||||
{
|
||||
timeRange: { from: 'now-15m', to: 'now' },
|
||||
|
@ -244,8 +232,6 @@ test('Cancelling custom time range action leaves state alone', async done => {
|
|||
});
|
||||
|
||||
test(`badge is compatible with embeddable that inherits from parent`, async () => {
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(TIME_RANGE_EMBEDDABLE, new TimeRangeEmbeddableFactory());
|
||||
const container = new TimeRangeContainer(
|
||||
{
|
||||
timeRange: { from: 'now-15m', to: 'now' },
|
||||
|
@ -279,8 +265,6 @@ test(`badge is compatible with embeddable that inherits from parent`, async () =
|
|||
|
||||
// TODO: uncomment when https://github.com/elastic/kibana/issues/43271 is fixed.
|
||||
// test('Embeddable that does not use time range in a container that has time range is incompatible', async () => {
|
||||
// const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
// embeddableFactories.set(HELLO_WORLD_EMBEDDABLE, new HelloWorldEmbeddableFactory());
|
||||
// const container = new TimeRangeContainer(
|
||||
// {
|
||||
// timeRange: { from: 'now-15m', to: 'now' },
|
||||
|
@ -315,8 +299,6 @@ test(`badge is compatible with embeddable that inherits from parent`, async () =
|
|||
// });
|
||||
|
||||
test('Attempting to execute on incompatible embeddable throws an error', async () => {
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(HELLO_WORLD_EMBEDDABLE, new HelloWorldEmbeddableFactory());
|
||||
const container = new HelloWorldContainer(
|
||||
{
|
||||
panels: {
|
||||
|
|
|
@ -9,17 +9,12 @@ import { findTestSubject } from '@elastic/eui/lib/test';
|
|||
import { skip } from 'rxjs/operators';
|
||||
import * as Rx from 'rxjs';
|
||||
import { mount } from 'enzyme';
|
||||
import { EmbeddableFactory } from '../../../../src/plugins/embeddable/public';
|
||||
import { TimeRangeEmbeddable, TimeRangeContainer, TIME_RANGE_EMBEDDABLE } from './test_helpers';
|
||||
import { TimeRangeEmbeddableFactory } from './test_helpers/time_range_embeddable_factory';
|
||||
import { CustomTimeRangeBadge } from './custom_time_range_badge';
|
||||
import { ReactElement } from 'react';
|
||||
import { nextTick } from 'test_utils/enzyme_helpers';
|
||||
|
||||
test('Removing custom time range from badge resets embeddable back to container time', async done => {
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(TIME_RANGE_EMBEDDABLE, new TimeRangeEmbeddableFactory());
|
||||
|
||||
const container = new TimeRangeContainer(
|
||||
{
|
||||
timeRange: { from: 'now-15m', to: 'now' },
|
||||
|
@ -79,8 +74,6 @@ test('Removing custom time range from badge resets embeddable back to container
|
|||
});
|
||||
|
||||
test(`badge is not compatible with embeddable that inherits from parent`, async () => {
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(TIME_RANGE_EMBEDDABLE, new TimeRangeEmbeddableFactory());
|
||||
const container = new TimeRangeContainer(
|
||||
{
|
||||
timeRange: { from: 'now-15m', to: 'now' },
|
||||
|
@ -113,8 +106,6 @@ test(`badge is not compatible with embeddable that inherits from parent`, async
|
|||
});
|
||||
|
||||
test(`badge is compatible with embeddable that has custom time range`, async () => {
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(TIME_RANGE_EMBEDDABLE, new TimeRangeEmbeddableFactory());
|
||||
const container = new TimeRangeContainer(
|
||||
{
|
||||
timeRange: { from: 'now-15m', to: 'now' },
|
||||
|
@ -148,8 +139,6 @@ test(`badge is compatible with embeddable that has custom time range`, async ()
|
|||
});
|
||||
|
||||
test('Attempting to execute on incompatible embeddable throws an error', async () => {
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
embeddableFactories.set(TIME_RANGE_EMBEDDABLE, new TimeRangeEmbeddableFactory());
|
||||
const container = new TimeRangeContainer(
|
||||
{
|
||||
timeRange: { from: 'now-15m', to: 'now' },
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import {
|
||||
EmbeddableInput,
|
||||
IContainer,
|
||||
EmbeddableFactory,
|
||||
EmbeddableFactoryDefinition,
|
||||
} from '../../../../../src/plugins/embeddable/public';
|
||||
import { TimeRange } from '../../../../../src/plugins/data/public';
|
||||
import { TIME_RANGE_EMBEDDABLE, TimeRangeEmbeddable } from './time_range_embeddable';
|
||||
|
@ -16,7 +16,8 @@ interface EmbeddableTimeRangeInput extends EmbeddableInput {
|
|||
timeRange: TimeRange;
|
||||
}
|
||||
|
||||
export class TimeRangeEmbeddableFactory extends EmbeddableFactory<EmbeddableTimeRangeInput> {
|
||||
export class TimeRangeEmbeddableFactory
|
||||
implements EmbeddableFactoryDefinition<EmbeddableTimeRangeInput> {
|
||||
public readonly type = TIME_RANGE_EMBEDDABLE;
|
||||
|
||||
public async isEditable() {
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EmbeddableFactory,
|
||||
IContainer,
|
||||
EmbeddableInput,
|
||||
EmbeddableFactoryDefinition,
|
||||
} from '../../../../../../src/plugins/embeddable/public';
|
||||
import { ResolverEmbeddable } from './embeddable';
|
||||
|
||||
export class ResolverEmbeddableFactory extends EmbeddableFactory {
|
||||
export class ResolverEmbeddableFactory implements EmbeddableFactoryDefinition {
|
||||
public readonly type = 'resolver';
|
||||
|
||||
public async isEditable() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue