mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 11:05:39 -04:00
Embeddable add panel examples (#57319)
* Embeddable add panel examples * add tests * Fix type error after merge * address code review comments Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
5946729097
commit
63cfffbe11
14 changed files with 428 additions and 79 deletions
|
@ -23,9 +23,13 @@ import {
|
||||||
MultiTaskTodoEmbeddable,
|
MultiTaskTodoEmbeddable,
|
||||||
MULTI_TASK_TODO_EMBEDDABLE,
|
MULTI_TASK_TODO_EMBEDDABLE,
|
||||||
MultiTaskTodoInput,
|
MultiTaskTodoInput,
|
||||||
|
MultiTaskTodoOutput,
|
||||||
} from './multi_task_todo_embeddable';
|
} from './multi_task_todo_embeddable';
|
||||||
|
|
||||||
export class MultiTaskTodoEmbeddableFactory extends EmbeddableFactory {
|
export class MultiTaskTodoEmbeddableFactory extends EmbeddableFactory<
|
||||||
|
MultiTaskTodoInput,
|
||||||
|
MultiTaskTodoOutput
|
||||||
|
> {
|
||||||
public readonly type = MULTI_TASK_TODO_EMBEDDABLE;
|
public readonly type = MULTI_TASK_TODO_EMBEDDABLE;
|
||||||
|
|
||||||
public isEditable() {
|
public isEditable() {
|
||||||
|
@ -36,6 +40,15 @@ export class MultiTaskTodoEmbeddableFactory extends EmbeddableFactory {
|
||||||
return new MultiTaskTodoEmbeddable(initialInput, parent);
|
return new MultiTaskTodoEmbeddable(initialInput, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check out todo_embeddable_factory for a better example that asks for data from
|
||||||
|
* the user. This just returns default data. That's okay too though, if you want to
|
||||||
|
* start with default data and expose an "edit" action to modify it.
|
||||||
|
*/
|
||||||
|
public async getExplicitInput() {
|
||||||
|
return { title: 'default title', tasks: ['Im default data'] };
|
||||||
|
}
|
||||||
|
|
||||||
public getDisplayName() {
|
public getDisplayName() {
|
||||||
return i18n.translate('embeddableExamples.multiTaskTodo.displayName', {
|
return i18n.translate('embeddableExamples.multiTaskTodo.displayName', {
|
||||||
defaultMessage: 'Multi-task todo item',
|
defaultMessage: 'Multi-task todo item',
|
||||||
|
|
|
@ -17,11 +17,20 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IEmbeddableSetup, IEmbeddableStart } from '../../../src/plugins/embeddable/public';
|
import {
|
||||||
|
IEmbeddableSetup,
|
||||||
|
IEmbeddableStart,
|
||||||
|
EmbeddableFactory,
|
||||||
|
} from '../../../src/plugins/embeddable/public';
|
||||||
import { Plugin, CoreSetup, CoreStart } from '../../../src/core/public';
|
import { Plugin, CoreSetup, CoreStart } from '../../../src/core/public';
|
||||||
import { HelloWorldEmbeddableFactory, HELLO_WORLD_EMBEDDABLE } from './hello_world';
|
import { HelloWorldEmbeddableFactory, HELLO_WORLD_EMBEDDABLE } from './hello_world';
|
||||||
import { TODO_EMBEDDABLE, TodoEmbeddableFactory } from './todo';
|
import { TODO_EMBEDDABLE, TodoEmbeddableFactory, TodoInput, TodoOutput } from './todo';
|
||||||
import { MULTI_TASK_TODO_EMBEDDABLE, MultiTaskTodoEmbeddableFactory } from './multi_task_todo';
|
import {
|
||||||
|
MULTI_TASK_TODO_EMBEDDABLE,
|
||||||
|
MultiTaskTodoEmbeddableFactory,
|
||||||
|
MultiTaskTodoOutput,
|
||||||
|
MultiTaskTodoInput,
|
||||||
|
} from './multi_task_todo';
|
||||||
import {
|
import {
|
||||||
SEARCHABLE_LIST_CONTAINER,
|
SEARCHABLE_LIST_CONTAINER,
|
||||||
SearchableListContainerFactory,
|
SearchableListContainerFactory,
|
||||||
|
@ -45,12 +54,9 @@ export class EmbeddableExamplesPlugin
|
||||||
new HelloWorldEmbeddableFactory()
|
new HelloWorldEmbeddableFactory()
|
||||||
);
|
);
|
||||||
|
|
||||||
deps.embeddable.registerEmbeddableFactory(TODO_EMBEDDABLE, new TodoEmbeddableFactory());
|
deps.embeddable.registerEmbeddableFactory<
|
||||||
|
EmbeddableFactory<MultiTaskTodoInput, MultiTaskTodoOutput>
|
||||||
deps.embeddable.registerEmbeddableFactory(
|
>(MULTI_TASK_TODO_EMBEDDABLE, new MultiTaskTodoEmbeddableFactory());
|
||||||
MULTI_TASK_TODO_EMBEDDABLE,
|
|
||||||
new MultiTaskTodoEmbeddableFactory()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public start(core: CoreStart, deps: EmbeddableExamplesStartDependencies) {
|
public start(core: CoreStart, deps: EmbeddableExamplesStartDependencies) {
|
||||||
|
@ -66,6 +72,11 @@ export class EmbeddableExamplesPlugin
|
||||||
LIST_CONTAINER,
|
LIST_CONTAINER,
|
||||||
new ListContainerFactory(deps.embeddable.getEmbeddableFactory)
|
new ListContainerFactory(deps.embeddable.getEmbeddableFactory)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
deps.embeddable.registerEmbeddableFactory<EmbeddableFactory<TodoInput, TodoOutput>>(
|
||||||
|
TODO_EMBEDDABLE,
|
||||||
|
new TodoEmbeddableFactory(core.overlays.openModal)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public stop() {}
|
public stop() {}
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
|
||||||
* license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright
|
|
||||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
|
||||||
* the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
* not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
|
||||||
import { IContainer, EmbeddableFactory } from '../../../../src/plugins/embeddable/public';
|
|
||||||
import { TodoEmbeddable, TODO_EMBEDDABLE, TodoInput } from './todo_embeddable';
|
|
||||||
|
|
||||||
export class TodoEmbeddableFactory extends EmbeddableFactory {
|
|
||||||
public readonly type = TODO_EMBEDDABLE;
|
|
||||||
|
|
||||||
public isEditable() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async create(initialInput: TodoInput, parent?: IContainer) {
|
|
||||||
return new TodoEmbeddable(initialInput, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getDisplayName() {
|
|
||||||
return i18n.translate('embeddableExamples.todo.displayName', {
|
|
||||||
defaultMessage: 'Todo item',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { EuiModalBody } from '@elastic/eui';
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
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 { TodoEmbeddable, TODO_EMBEDDABLE, TodoInput, TodoOutput } from './todo_embeddable';
|
||||||
|
|
||||||
|
function TaskInput({ onSave }: { onSave: (task: string) => void }) {
|
||||||
|
const [task, setTask] = useState('');
|
||||||
|
return (
|
||||||
|
<EuiModalBody>
|
||||||
|
<EuiFieldText
|
||||||
|
data-test-subj="taskInputField"
|
||||||
|
value={task}
|
||||||
|
placeholder="Enter task here"
|
||||||
|
onChange={e => setTask(e.target.value)}
|
||||||
|
/>
|
||||||
|
<EuiButton data-test-subj="createTodoEmbeddable" onClick={() => onSave(task)}>
|
||||||
|
Save
|
||||||
|
</EuiButton>
|
||||||
|
</EuiModalBody>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TodoEmbeddableFactory extends EmbeddableFactory<
|
||||||
|
TodoInput,
|
||||||
|
TodoOutput,
|
||||||
|
TodoEmbeddable
|
||||||
|
> {
|
||||||
|
public readonly type = TODO_EMBEDDABLE;
|
||||||
|
|
||||||
|
constructor(private openModal: OverlayStart['openModal']) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public isEditable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async create(initialInput: TodoInput, parent?: IContainer) {
|
||||||
|
return new TodoEmbeddable(initialInput, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used when dynamically creating a new embeddable to add to a
|
||||||
|
* container. Some input may be inherited from the container, but not all. This can be
|
||||||
|
* used to collect specific embeddable input that the container will not provide, like
|
||||||
|
* in this case, the task string.
|
||||||
|
*/
|
||||||
|
public async getExplicitInput() {
|
||||||
|
return new Promise<{ task: string }>(resolve => {
|
||||||
|
const onSave = (task: string) => resolve({ task });
|
||||||
|
const overlay = this.openModal(
|
||||||
|
toMountPoint(
|
||||||
|
<TaskInput
|
||||||
|
onSave={(task: string) => {
|
||||||
|
onSave(task);
|
||||||
|
overlay.close();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getDisplayName() {
|
||||||
|
return i18n.translate('embeddableExamples.todo.displayName', {
|
||||||
|
defaultMessage: 'Todo item',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,6 @@
|
||||||
"configPath": ["embeddable_explorer"],
|
"configPath": ["embeddable_explorer"],
|
||||||
"server": false,
|
"server": false,
|
||||||
"ui": true,
|
"ui": true,
|
||||||
"requiredPlugins": ["embeddable", "embeddableExamples"],
|
"requiredPlugins": ["uiActions", "inspector", "embeddable", "embeddableExamples"],
|
||||||
"optionalPlugins": []
|
"optionalPlugins": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,21 @@ import { BrowserRouter as Router, Route, withRouter, RouteComponentProps } from
|
||||||
|
|
||||||
import { EuiPage, EuiPageSideBar, EuiSideNav } from '@elastic/eui';
|
import { EuiPage, EuiPageSideBar, EuiSideNav } from '@elastic/eui';
|
||||||
|
|
||||||
import { IEmbeddableStart } from 'src/plugins/embeddable/public';
|
import { IEmbeddableStart } from '../../../src/plugins/embeddable/public';
|
||||||
import { AppMountContext, AppMountParameters, CoreStart } from '../../../src/core/public';
|
import { UiActionsStart } from '../../../src/plugins/ui_actions/public';
|
||||||
|
import { Start as InspectorStartContract } from '../../../src/plugins/inspector/public';
|
||||||
|
import {
|
||||||
|
AppMountContext,
|
||||||
|
AppMountParameters,
|
||||||
|
CoreStart,
|
||||||
|
SavedObjectsStart,
|
||||||
|
IUiSettingsClient,
|
||||||
|
OverlayStart,
|
||||||
|
} from '../../../src/core/public';
|
||||||
import { HelloWorldEmbeddableExample } from './hello_world_embeddable_example';
|
import { HelloWorldEmbeddableExample } from './hello_world_embeddable_example';
|
||||||
import { TodoEmbeddableExample } from './todo_embeddable_example';
|
import { TodoEmbeddableExample } from './todo_embeddable_example';
|
||||||
import { ListContainerExample } from './list_container_example';
|
import { ListContainerExample } from './list_container_example';
|
||||||
|
import { EmbeddablePanelExample } from './embeddable_panel_example';
|
||||||
|
|
||||||
interface PageDef {
|
interface PageDef {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -61,15 +71,29 @@ const Nav = withRouter(({ history, navigateToApp, pages }: NavProps) => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
basename: string;
|
||||||
|
navigateToApp: CoreStart['application']['navigateToApp'];
|
||||||
|
embeddableApi: IEmbeddableStart;
|
||||||
|
uiActionsApi: UiActionsStart;
|
||||||
|
overlays: OverlayStart;
|
||||||
|
notifications: CoreStart['notifications'];
|
||||||
|
inspector: InspectorStartContract;
|
||||||
|
savedObject: SavedObjectsStart;
|
||||||
|
uiSettingsClient: IUiSettingsClient;
|
||||||
|
}
|
||||||
|
|
||||||
const EmbeddableExplorerApp = ({
|
const EmbeddableExplorerApp = ({
|
||||||
basename,
|
basename,
|
||||||
navigateToApp,
|
navigateToApp,
|
||||||
embeddableApi,
|
embeddableApi,
|
||||||
}: {
|
inspector,
|
||||||
basename: string;
|
uiSettingsClient,
|
||||||
navigateToApp: CoreStart['application']['navigateToApp'];
|
savedObject,
|
||||||
embeddableApi: IEmbeddableStart;
|
overlays,
|
||||||
}) => {
|
uiActionsApi,
|
||||||
|
notifications,
|
||||||
|
}: Props) => {
|
||||||
const pages: PageDef[] = [
|
const pages: PageDef[] = [
|
||||||
{
|
{
|
||||||
title: 'Hello world embeddable',
|
title: 'Hello world embeddable',
|
||||||
|
@ -90,6 +114,22 @@ const EmbeddableExplorerApp = ({
|
||||||
id: 'listContainerSection',
|
id: 'listContainerSection',
|
||||||
component: <ListContainerExample getEmbeddableFactory={embeddableApi.getEmbeddableFactory} />,
|
component: <ListContainerExample getEmbeddableFactory={embeddableApi.getEmbeddableFactory} />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Dynamically adding children to a container',
|
||||||
|
id: 'embeddablePanelExamplae',
|
||||||
|
component: (
|
||||||
|
<EmbeddablePanelExample
|
||||||
|
uiActionsApi={uiActionsApi}
|
||||||
|
getAllEmbeddableFactories={embeddableApi.getEmbeddableFactories}
|
||||||
|
getEmbeddableFactory={embeddableApi.getEmbeddableFactory}
|
||||||
|
overlays={overlays}
|
||||||
|
uiSettingsClient={uiSettingsClient}
|
||||||
|
savedObject={savedObject}
|
||||||
|
notifications={notifications}
|
||||||
|
inspector={inspector}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const routes = pages.map((page, i) => (
|
const routes = pages.map((page, i) => (
|
||||||
|
@ -108,19 +148,8 @@ const EmbeddableExplorerApp = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const renderApp = (
|
export const renderApp = (props: Props, element: AppMountParameters['element']) => {
|
||||||
core: CoreStart,
|
ReactDOM.render(<EmbeddableExplorerApp {...props} />, element);
|
||||||
embeddableApi: IEmbeddableStart,
|
|
||||||
{ appBasePath, element }: AppMountParameters
|
|
||||||
) => {
|
|
||||||
ReactDOM.render(
|
|
||||||
<EmbeddableExplorerApp
|
|
||||||
basename={appBasePath}
|
|
||||||
navigateToApp={core.application.navigateToApp}
|
|
||||||
embeddableApi={embeddableApi}
|
|
||||||
/>,
|
|
||||||
element
|
|
||||||
);
|
|
||||||
|
|
||||||
return () => ReactDOM.unmountComponentAtNode(element);
|
return () => ReactDOM.unmountComponentAtNode(element);
|
||||||
};
|
};
|
||||||
|
|
164
examples/embeddable_explorer/public/embeddable_panel_example.tsx
Normal file
164
examples/embeddable_explorer/public/embeddable_panel_example.tsx
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
import {
|
||||||
|
EuiPanel,
|
||||||
|
EuiPageBody,
|
||||||
|
EuiPageContent,
|
||||||
|
EuiPageContentBody,
|
||||||
|
EuiPageHeader,
|
||||||
|
EuiPageHeaderSection,
|
||||||
|
EuiTitle,
|
||||||
|
EuiText,
|
||||||
|
} from '@elastic/eui';
|
||||||
|
import { EuiSpacer } from '@elastic/eui';
|
||||||
|
import { OverlayStart, CoreStart, SavedObjectsStart, IUiSettingsClient } from 'kibana/public';
|
||||||
|
import {
|
||||||
|
GetEmbeddableFactory,
|
||||||
|
EmbeddablePanel,
|
||||||
|
IEmbeddableStart,
|
||||||
|
IEmbeddable,
|
||||||
|
} from '../../../src/plugins/embeddable/public';
|
||||||
|
import {
|
||||||
|
HELLO_WORLD_EMBEDDABLE,
|
||||||
|
TODO_EMBEDDABLE,
|
||||||
|
MULTI_TASK_TODO_EMBEDDABLE,
|
||||||
|
SEARCHABLE_LIST_CONTAINER,
|
||||||
|
} from '../../embeddable_examples/public';
|
||||||
|
import { UiActionsStart } from '../../../src/plugins/ui_actions/public';
|
||||||
|
import { Start as InspectorStartContract } from '../../../src/plugins/inspector/public';
|
||||||
|
import { getSavedObjectFinder } from '../../../src/plugins/saved_objects/public';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
getAllEmbeddableFactories: IEmbeddableStart['getEmbeddableFactories'];
|
||||||
|
getEmbeddableFactory: GetEmbeddableFactory;
|
||||||
|
uiActionsApi: UiActionsStart;
|
||||||
|
overlays: OverlayStart;
|
||||||
|
notifications: CoreStart['notifications'];
|
||||||
|
inspector: InspectorStartContract;
|
||||||
|
savedObject: SavedObjectsStart;
|
||||||
|
uiSettingsClient: IUiSettingsClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EmbeddablePanelExample({
|
||||||
|
inspector,
|
||||||
|
notifications,
|
||||||
|
overlays,
|
||||||
|
getAllEmbeddableFactories,
|
||||||
|
getEmbeddableFactory,
|
||||||
|
uiActionsApi,
|
||||||
|
savedObject,
|
||||||
|
uiSettingsClient,
|
||||||
|
}: Props) {
|
||||||
|
const searchableInput = {
|
||||||
|
id: '1',
|
||||||
|
title: 'My searchable todo list',
|
||||||
|
panels: {
|
||||||
|
'1': {
|
||||||
|
type: HELLO_WORLD_EMBEDDABLE,
|
||||||
|
explicitInput: {
|
||||||
|
id: '1',
|
||||||
|
title: 'Hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'2': {
|
||||||
|
type: TODO_EMBEDDABLE,
|
||||||
|
explicitInput: {
|
||||||
|
id: '2',
|
||||||
|
task: 'Goes out on Wednesdays!',
|
||||||
|
icon: 'broom',
|
||||||
|
title: 'Take out the trash',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
type: MULTI_TASK_TODO_EMBEDDABLE,
|
||||||
|
explicitInput: {
|
||||||
|
id: '3',
|
||||||
|
icon: 'searchProfilerApp',
|
||||||
|
title: 'Learn more',
|
||||||
|
tasks: ['Go to school', 'Watch planet earth', 'Read the encyclopedia'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const [embeddable, setEmbeddable] = useState<IEmbeddable | undefined>(undefined);
|
||||||
|
|
||||||
|
const ref = useRef(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
ref.current = true;
|
||||||
|
if (!embeddable) {
|
||||||
|
const factory = getEmbeddableFactory(SEARCHABLE_LIST_CONTAINER);
|
||||||
|
const promise = factory?.create(searchableInput);
|
||||||
|
if (promise) {
|
||||||
|
promise.then(e => {
|
||||||
|
if (ref.current) {
|
||||||
|
setEmbeddable(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
ref.current = false;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EuiPageBody>
|
||||||
|
<EuiPageHeader>
|
||||||
|
<EuiPageHeaderSection>
|
||||||
|
<EuiTitle size="l">
|
||||||
|
<h1>The embeddable panel component</h1>
|
||||||
|
</EuiTitle>
|
||||||
|
</EuiPageHeaderSection>
|
||||||
|
</EuiPageHeader>
|
||||||
|
<EuiPageContent>
|
||||||
|
<EuiPageContentBody>
|
||||||
|
<EuiText>
|
||||||
|
You can render your embeddable inside the EmbeddablePanel component. This adds some
|
||||||
|
extra rendering and offers a context menu with pluggable actions. Using EmbeddablePanel
|
||||||
|
to render your embeddable means you get access to the "e;Add panel flyout"e;.
|
||||||
|
Now you can see how to add embeddables to your container, and how
|
||||||
|
"e;getExplicitInput"e; is used to grab input not provided by the container.
|
||||||
|
</EuiText>
|
||||||
|
<EuiPanel data-test-subj="embeddedPanelExample" paddingSize="none" role="figure">
|
||||||
|
{embeddable ? (
|
||||||
|
<EmbeddablePanel
|
||||||
|
embeddable={embeddable}
|
||||||
|
getActions={uiActionsApi.getTriggerCompatibleActions}
|
||||||
|
getEmbeddableFactory={getEmbeddableFactory}
|
||||||
|
getAllEmbeddableFactories={getAllEmbeddableFactories}
|
||||||
|
overlays={overlays}
|
||||||
|
notifications={notifications}
|
||||||
|
inspector={inspector}
|
||||||
|
SavedObjectFinder={getSavedObjectFinder(savedObject, uiSettingsClient)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<EuiText>Loading...</EuiText>
|
||||||
|
)}
|
||||||
|
</EuiPanel>
|
||||||
|
|
||||||
|
<EuiSpacer />
|
||||||
|
</EuiPageContentBody>
|
||||||
|
</EuiPageContent>
|
||||||
|
</EuiPageBody>
|
||||||
|
);
|
||||||
|
}
|
|
@ -60,7 +60,7 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) {
|
||||||
type: TODO_EMBEDDABLE,
|
type: TODO_EMBEDDABLE,
|
||||||
explicitInput: {
|
explicitInput: {
|
||||||
id: '2',
|
id: '2',
|
||||||
task: 'Goes out on Wenesdays!',
|
task: 'Goes out on Wednesdays!',
|
||||||
icon: 'broom',
|
icon: 'broom',
|
||||||
title: 'Take out the trash',
|
title: 'Take out the trash',
|
||||||
},
|
},
|
||||||
|
@ -91,7 +91,7 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) {
|
||||||
type: TODO_EMBEDDABLE,
|
type: TODO_EMBEDDABLE,
|
||||||
explicitInput: {
|
explicitInput: {
|
||||||
id: '2',
|
id: '2',
|
||||||
task: 'Goes out on Wenesdays!',
|
task: 'Goes out on Wednesdays!',
|
||||||
icon: 'broom',
|
icon: 'broom',
|
||||||
title: 'Take out the trash',
|
title: 'Take out the trash',
|
||||||
},
|
},
|
||||||
|
@ -102,7 +102,7 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) {
|
||||||
id: '3',
|
id: '3',
|
||||||
icon: 'searchProfilerApp',
|
icon: 'searchProfilerApp',
|
||||||
title: 'Learn more',
|
title: 'Learn more',
|
||||||
tasks: ['Go to school', 'Watch planet earth', 'Read the encylopedia'],
|
tasks: ['Go to school', 'Watch planet earth', 'Read the encyclopedia'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -151,6 +151,11 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) {
|
||||||
The first HelloWorldEmbeddable does not emit the hasMatch output variable, so the
|
The first HelloWorldEmbeddable does not emit the hasMatch output variable, so the
|
||||||
container chooses to hide it.
|
container chooses to hide it.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Check out the "e;Dynamically adding children"e; section, to see how to add
|
||||||
|
children to this container, and see it rendered inside an `EmbeddablePanel` component.
|
||||||
|
</p>
|
||||||
</EuiText>
|
</EuiText>
|
||||||
|
|
||||||
<EuiSpacer />
|
<EuiSpacer />
|
||||||
|
|
|
@ -18,17 +18,38 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public';
|
import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public';
|
||||||
|
import { UiActionsService } from '../../../src/plugins/ui_actions/public';
|
||||||
import { IEmbeddableStart } from '../../../src/plugins/embeddable/public';
|
import { IEmbeddableStart } from '../../../src/plugins/embeddable/public';
|
||||||
|
import { Start as InspectorStart } from '../../../src/plugins/inspector/public';
|
||||||
|
|
||||||
export class EmbeddableExplorerPlugin implements Plugin {
|
interface StartDeps {
|
||||||
public setup(core: CoreSetup<{ embeddable: IEmbeddableStart }>) {
|
uiActions: UiActionsService;
|
||||||
|
embeddable: IEmbeddableStart;
|
||||||
|
inspector: InspectorStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EmbeddableExplorerPlugin implements Plugin<void, void, {}, StartDeps> {
|
||||||
|
public setup(core: CoreSetup<StartDeps>) {
|
||||||
core.application.register({
|
core.application.register({
|
||||||
id: 'embeddableExplorer',
|
id: 'embeddableExplorer',
|
||||||
title: 'Embeddable explorer',
|
title: 'Embeddable explorer',
|
||||||
async mount(params: AppMountParameters) {
|
async mount(params: AppMountParameters) {
|
||||||
const [coreStart, depsStart] = await core.getStartServices();
|
const [coreStart, depsStart] = await core.getStartServices();
|
||||||
const { renderApp } = await import('./app');
|
const { renderApp } = await import('./app');
|
||||||
return renderApp(coreStart, depsStart.embeddable, params);
|
return renderApp(
|
||||||
|
{
|
||||||
|
notifications: coreStart.notifications,
|
||||||
|
inspector: depsStart.inspector,
|
||||||
|
embeddableApi: depsStart.embeddable,
|
||||||
|
uiActionsApi: depsStart.uiActions,
|
||||||
|
basename: params.appBasePath,
|
||||||
|
uiSettingsClient: coreStart.uiSettings,
|
||||||
|
savedObject: coreStart.savedObjects,
|
||||||
|
overlays: coreStart.overlays,
|
||||||
|
navigateToApp: coreStart.application.navigateToApp,
|
||||||
|
},
|
||||||
|
params.element
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,10 @@ export interface EmbeddableApi {
|
||||||
getEmbeddableFactory: (embeddableFactoryId: string) => EmbeddableFactory;
|
getEmbeddableFactory: (embeddableFactoryId: string) => EmbeddableFactory;
|
||||||
getEmbeddableFactories: GetEmbeddableFactories;
|
getEmbeddableFactories: GetEmbeddableFactories;
|
||||||
// TODO: Make `registerEmbeddableFactory` receive only `factory` argument.
|
// TODO: Make `registerEmbeddableFactory` receive only `factory` argument.
|
||||||
registerEmbeddableFactory: (id: string, factory: EmbeddableFactory) => void;
|
registerEmbeddableFactory: <TEmbeddableFactory extends EmbeddableFactory>(
|
||||||
|
id: string,
|
||||||
|
factory: TEmbeddableFactory
|
||||||
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EmbeddableDependencies {
|
export interface EmbeddableDependencies {
|
||||||
|
|
|
@ -33,6 +33,13 @@ export default async function({ readConfigFile }) {
|
||||||
...functionalConfig.get('services'),
|
...functionalConfig.get('services'),
|
||||||
...services,
|
...services,
|
||||||
},
|
},
|
||||||
|
uiSettings: {
|
||||||
|
defaults: {
|
||||||
|
'accessibility:disableAnimations': true,
|
||||||
|
'dateFormat:tz': 'UTC',
|
||||||
|
'telemetry:optIn': false,
|
||||||
|
},
|
||||||
|
},
|
||||||
pageObjects: functionalConfig.get('pageObjects'),
|
pageObjects: functionalConfig.get('pageObjects'),
|
||||||
servers: functionalConfig.get('servers'),
|
servers: functionalConfig.get('servers'),
|
||||||
esTestCluster: functionalConfig.get('esTestCluster'),
|
esTestCluster: functionalConfig.get('esTestCluster'),
|
||||||
|
|
43
test/examples/embeddables/adding_children.ts
Normal file
43
test/examples/embeddables/adding_children.ts
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* 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 expect from '@kbn/expect';
|
||||||
|
import { PluginFunctionalProviderContext } from 'test/plugin_functional/services';
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
export default function({ getService }: PluginFunctionalProviderContext) {
|
||||||
|
const testSubjects = getService('testSubjects');
|
||||||
|
|
||||||
|
describe('creating and adding children', () => {
|
||||||
|
before(async () => {
|
||||||
|
await testSubjects.click('embeddablePanelExamplae');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Can create a new child', async () => {
|
||||||
|
await testSubjects.click('embeddablePanelToggleMenuIcon');
|
||||||
|
await testSubjects.click('embeddablePanelAction-ADD_PANEL_ACTION_ID');
|
||||||
|
await testSubjects.click('createNew');
|
||||||
|
await testSubjects.click('createNew-TODO_EMBEDDABLE');
|
||||||
|
await testSubjects.setValue('taskInputField', 'new task');
|
||||||
|
await testSubjects.click('createTodoEmbeddable');
|
||||||
|
const tasks = await testSubjects.getVisibleTextAll('todoEmbeddableTask');
|
||||||
|
expect(tasks).to.eql(['Goes out on Wednesdays!', 'new task']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -39,5 +39,6 @@ export default function({
|
||||||
loadTestFile(require.resolve('./hello_world_embeddable'));
|
loadTestFile(require.resolve('./hello_world_embeddable'));
|
||||||
loadTestFile(require.resolve('./todo_embeddable'));
|
loadTestFile(require.resolve('./todo_embeddable'));
|
||||||
loadTestFile(require.resolve('./list_container'));
|
loadTestFile(require.resolve('./list_container'));
|
||||||
|
loadTestFile(require.resolve('./adding_children'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ export default function({ getService }: PluginFunctionalProviderContext) {
|
||||||
expect(text).to.eql(['HELLO WORLD!', 'HELLO WORLD!']);
|
expect(text).to.eql(['HELLO WORLD!', 'HELLO WORLD!']);
|
||||||
|
|
||||||
const tasks = await testSubjects.getVisibleTextAll('multiTaskTodoTask');
|
const tasks = await testSubjects.getVisibleTextAll('multiTaskTodoTask');
|
||||||
expect(tasks).to.eql(['Go to school', 'Watch planet earth', 'Read the encylopedia']);
|
expect(tasks).to.eql(['Go to school', 'Watch planet earth', 'Read the encyclopedia']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue