From 63cfffbe11c09388e600e67bafbd0aed3e893ece Mon Sep 17 00:00:00 2001
From: Stacey Gammon
Date: Wed, 19 Feb 2020 15:16:58 -0500
Subject: [PATCH] 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
---
.../multi_task_todo_embeddable_factory.ts | 15 +-
examples/embeddable_examples/public/plugin.ts | 29 +++-
.../public/todo/todo_embeddable_factory.ts | 40 -----
.../public/todo/todo_embeddable_factory.tsx | 92 ++++++++++
examples/embeddable_explorer/kibana.json | 2 +-
examples/embeddable_explorer/public/app.tsx | 69 +++++---
.../public/embeddable_panel_example.tsx | 164 ++++++++++++++++++
.../public/list_container_example.tsx | 11 +-
.../embeddable_explorer/public/plugin.tsx | 27 ++-
src/plugins/embeddable/public/api/types.ts | 5 +-
test/examples/config.js | 7 +
test/examples/embeddables/adding_children.ts | 43 +++++
test/examples/embeddables/index.ts | 1 +
test/examples/embeddables/list_container.ts | 2 +-
14 files changed, 428 insertions(+), 79 deletions(-)
delete mode 100644 examples/embeddable_examples/public/todo/todo_embeddable_factory.ts
create mode 100644 examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx
create mode 100644 examples/embeddable_explorer/public/embeddable_panel_example.tsx
create mode 100644 test/examples/embeddables/adding_children.ts
diff --git a/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts b/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts
index 37ac63e380f9..a54201b157a6 100644
--- a/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts
+++ b/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts
@@ -23,9 +23,13 @@ import {
MultiTaskTodoEmbeddable,
MULTI_TASK_TODO_EMBEDDABLE,
MultiTaskTodoInput,
+ MultiTaskTodoOutput,
} 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 isEditable() {
@@ -36,6 +40,15 @@ export class MultiTaskTodoEmbeddableFactory extends EmbeddableFactory {
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() {
return i18n.translate('embeddableExamples.multiTaskTodo.displayName', {
defaultMessage: 'Multi-task todo item',
diff --git a/examples/embeddable_examples/public/plugin.ts b/examples/embeddable_examples/public/plugin.ts
index e75165bfbef1..b7a4f5c078d5 100644
--- a/examples/embeddable_examples/public/plugin.ts
+++ b/examples/embeddable_examples/public/plugin.ts
@@ -17,11 +17,20 @@
* 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 { HelloWorldEmbeddableFactory, HELLO_WORLD_EMBEDDABLE } from './hello_world';
-import { TODO_EMBEDDABLE, TodoEmbeddableFactory } from './todo';
-import { MULTI_TASK_TODO_EMBEDDABLE, MultiTaskTodoEmbeddableFactory } from './multi_task_todo';
+import { TODO_EMBEDDABLE, TodoEmbeddableFactory, TodoInput, TodoOutput } from './todo';
+import {
+ MULTI_TASK_TODO_EMBEDDABLE,
+ MultiTaskTodoEmbeddableFactory,
+ MultiTaskTodoOutput,
+ MultiTaskTodoInput,
+} from './multi_task_todo';
import {
SEARCHABLE_LIST_CONTAINER,
SearchableListContainerFactory,
@@ -45,12 +54,9 @@ export class EmbeddableExamplesPlugin
new HelloWorldEmbeddableFactory()
);
- deps.embeddable.registerEmbeddableFactory(TODO_EMBEDDABLE, new TodoEmbeddableFactory());
-
- deps.embeddable.registerEmbeddableFactory(
- MULTI_TASK_TODO_EMBEDDABLE,
- new MultiTaskTodoEmbeddableFactory()
- );
+ deps.embeddable.registerEmbeddableFactory<
+ EmbeddableFactory
+ >(MULTI_TASK_TODO_EMBEDDABLE, new MultiTaskTodoEmbeddableFactory());
}
public start(core: CoreStart, deps: EmbeddableExamplesStartDependencies) {
@@ -66,6 +72,11 @@ export class EmbeddableExamplesPlugin
LIST_CONTAINER,
new ListContainerFactory(deps.embeddable.getEmbeddableFactory)
);
+
+ deps.embeddable.registerEmbeddableFactory>(
+ TODO_EMBEDDABLE,
+ new TodoEmbeddableFactory(core.overlays.openModal)
+ );
}
public stop() {}
diff --git a/examples/embeddable_examples/public/todo/todo_embeddable_factory.ts b/examples/embeddable_examples/public/todo/todo_embeddable_factory.ts
deleted file mode 100644
index 386b3f296d99..000000000000
--- a/examples/embeddable_examples/public/todo/todo_embeddable_factory.ts
+++ /dev/null
@@ -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',
- });
- }
-}
diff --git a/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx b/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx
new file mode 100644
index 000000000000..dd2168bb39ee
--- /dev/null
+++ b/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx
@@ -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 (
+
+ setTask(e.target.value)}
+ />
+ onSave(task)}>
+ Save
+
+
+ );
+}
+
+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(
+ {
+ onSave(task);
+ overlay.close();
+ }}
+ />
+ )
+ );
+ });
+ }
+
+ public getDisplayName() {
+ return i18n.translate('embeddableExamples.todo.displayName', {
+ defaultMessage: 'Todo item',
+ });
+ }
+}
diff --git a/examples/embeddable_explorer/kibana.json b/examples/embeddable_explorer/kibana.json
index 4ca63e1a3624..6c27bcd39f12 100644
--- a/examples/embeddable_explorer/kibana.json
+++ b/examples/embeddable_explorer/kibana.json
@@ -5,6 +5,6 @@
"configPath": ["embeddable_explorer"],
"server": false,
"ui": true,
- "requiredPlugins": ["embeddable", "embeddableExamples"],
+ "requiredPlugins": ["uiActions", "inspector", "embeddable", "embeddableExamples"],
"optionalPlugins": []
}
diff --git a/examples/embeddable_explorer/public/app.tsx b/examples/embeddable_explorer/public/app.tsx
index be27fd04556e..da7e8cc188e3 100644
--- a/examples/embeddable_explorer/public/app.tsx
+++ b/examples/embeddable_explorer/public/app.tsx
@@ -23,11 +23,21 @@ import { BrowserRouter as Router, Route, withRouter, RouteComponentProps } from
import { EuiPage, EuiPageSideBar, EuiSideNav } from '@elastic/eui';
-import { IEmbeddableStart } from 'src/plugins/embeddable/public';
-import { AppMountContext, AppMountParameters, CoreStart } from '../../../src/core/public';
+import { IEmbeddableStart } from '../../../src/plugins/embeddable/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 { TodoEmbeddableExample } from './todo_embeddable_example';
import { ListContainerExample } from './list_container_example';
+import { EmbeddablePanelExample } from './embeddable_panel_example';
interface PageDef {
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 = ({
basename,
navigateToApp,
embeddableApi,
-}: {
- basename: string;
- navigateToApp: CoreStart['application']['navigateToApp'];
- embeddableApi: IEmbeddableStart;
-}) => {
+ inspector,
+ uiSettingsClient,
+ savedObject,
+ overlays,
+ uiActionsApi,
+ notifications,
+}: Props) => {
const pages: PageDef[] = [
{
title: 'Hello world embeddable',
@@ -90,6 +114,22 @@ const EmbeddableExplorerApp = ({
id: 'listContainerSection',
component: ,
},
+ {
+ title: 'Dynamically adding children to a container',
+ id: 'embeddablePanelExamplae',
+ component: (
+
+ ),
+ },
];
const routes = pages.map((page, i) => (
@@ -108,19 +148,8 @@ const EmbeddableExplorerApp = ({
);
};
-export const renderApp = (
- core: CoreStart,
- embeddableApi: IEmbeddableStart,
- { appBasePath, element }: AppMountParameters
-) => {
- ReactDOM.render(
- ,
- element
- );
+export const renderApp = (props: Props, element: AppMountParameters['element']) => {
+ ReactDOM.render(, element);
return () => ReactDOM.unmountComponentAtNode(element);
};
diff --git a/examples/embeddable_explorer/public/embeddable_panel_example.tsx b/examples/embeddable_explorer/public/embeddable_panel_example.tsx
new file mode 100644
index 000000000000..e6687d8563f5
--- /dev/null
+++ b/examples/embeddable_explorer/public/embeddable_panel_example.tsx
@@ -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(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 (
+
+
+
+
+ The embeddable panel component
+
+
+
+
+
+
+ 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.
+
+
+ {embeddable ? (
+
+ ) : (
+ Loading...
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/examples/embeddable_explorer/public/list_container_example.tsx b/examples/embeddable_explorer/public/list_container_example.tsx
index 49cfae0d4e45..2c7b12a27d96 100644
--- a/examples/embeddable_explorer/public/list_container_example.tsx
+++ b/examples/embeddable_explorer/public/list_container_example.tsx
@@ -60,7 +60,7 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) {
type: TODO_EMBEDDABLE,
explicitInput: {
id: '2',
- task: 'Goes out on Wenesdays!',
+ task: 'Goes out on Wednesdays!',
icon: 'broom',
title: 'Take out the trash',
},
@@ -91,7 +91,7 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) {
type: TODO_EMBEDDABLE,
explicitInput: {
id: '2',
- task: 'Goes out on Wenesdays!',
+ task: 'Goes out on Wednesdays!',
icon: 'broom',
title: 'Take out the trash',
},
@@ -102,7 +102,7 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) {
id: '3',
icon: 'searchProfilerApp',
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
container chooses to hide it.
+
+
+ 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.
+
diff --git a/examples/embeddable_explorer/public/plugin.tsx b/examples/embeddable_explorer/public/plugin.tsx
index 2576dea0cadb..1294e0c89c9e 100644
--- a/examples/embeddable_explorer/public/plugin.tsx
+++ b/examples/embeddable_explorer/public/plugin.tsx
@@ -18,17 +18,38 @@
*/
import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public';
+import { UiActionsService } from '../../../src/plugins/ui_actions/public';
import { IEmbeddableStart } from '../../../src/plugins/embeddable/public';
+import { Start as InspectorStart } from '../../../src/plugins/inspector/public';
-export class EmbeddableExplorerPlugin implements Plugin {
- public setup(core: CoreSetup<{ embeddable: IEmbeddableStart }>) {
+interface StartDeps {
+ uiActions: UiActionsService;
+ embeddable: IEmbeddableStart;
+ inspector: InspectorStart;
+}
+
+export class EmbeddableExplorerPlugin implements Plugin {
+ public setup(core: CoreSetup) {
core.application.register({
id: 'embeddableExplorer',
title: 'Embeddable explorer',
async mount(params: AppMountParameters) {
const [coreStart, depsStart] = await core.getStartServices();
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
+ );
},
});
}
diff --git a/src/plugins/embeddable/public/api/types.ts b/src/plugins/embeddable/public/api/types.ts
index 30fa49578541..179d96a4aff8 100644
--- a/src/plugins/embeddable/public/api/types.ts
+++ b/src/plugins/embeddable/public/api/types.ts
@@ -24,7 +24,10 @@ export interface EmbeddableApi {
getEmbeddableFactory: (embeddableFactoryId: string) => EmbeddableFactory;
getEmbeddableFactories: GetEmbeddableFactories;
// TODO: Make `registerEmbeddableFactory` receive only `factory` argument.
- registerEmbeddableFactory: (id: string, factory: EmbeddableFactory) => void;
+ registerEmbeddableFactory: (
+ id: string,
+ factory: TEmbeddableFactory
+ ) => void;
}
export interface EmbeddableDependencies {
diff --git a/test/examples/config.js b/test/examples/config.js
index d9411be26793..49d75da28607 100644
--- a/test/examples/config.js
+++ b/test/examples/config.js
@@ -33,6 +33,13 @@ export default async function({ readConfigFile }) {
...functionalConfig.get('services'),
...services,
},
+ uiSettings: {
+ defaults: {
+ 'accessibility:disableAnimations': true,
+ 'dateFormat:tz': 'UTC',
+ 'telemetry:optIn': false,
+ },
+ },
pageObjects: functionalConfig.get('pageObjects'),
servers: functionalConfig.get('servers'),
esTestCluster: functionalConfig.get('esTestCluster'),
diff --git a/test/examples/embeddables/adding_children.ts b/test/examples/embeddables/adding_children.ts
new file mode 100644
index 000000000000..8f4951b0e22f
--- /dev/null
+++ b/test/examples/embeddables/adding_children.ts
@@ -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']);
+ });
+ });
+}
diff --git a/test/examples/embeddables/index.ts b/test/examples/embeddables/index.ts
index ff7997b3b01d..8ad0961fcc3b 100644
--- a/test/examples/embeddables/index.ts
+++ b/test/examples/embeddables/index.ts
@@ -39,5 +39,6 @@ export default function({
loadTestFile(require.resolve('./hello_world_embeddable'));
loadTestFile(require.resolve('./todo_embeddable'));
loadTestFile(require.resolve('./list_container'));
+ loadTestFile(require.resolve('./adding_children'));
});
}
diff --git a/test/examples/embeddables/list_container.ts b/test/examples/embeddables/list_container.ts
index 79e613164790..b1b91ad2c37f 100644
--- a/test/examples/embeddables/list_container.ts
+++ b/test/examples/embeddables/list_container.ts
@@ -45,7 +45,7 @@ export default function({ getService }: PluginFunctionalProviderContext) {
expect(text).to.eql(['HELLO WORLD!', 'HELLO WORLD!']);
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']);
});
});