Skip to content

Commit

Permalink
Add embeddable via saved object example (#61692)
Browse files Browse the repository at this point in the history
* Add embeddable via saved object example

* give todoRefEmbed a different name from the by value one

* fix types

* fix order of unmounting

Co-authored-by: Christos Nasikas <christos.nasikas@elastic.co>
  • Loading branch information
stacey-gammon and cnasikas committed Apr 15, 2020
1 parent 00a1144 commit d040448
Show file tree
Hide file tree
Showing 31 changed files with 679 additions and 90 deletions.
20 changes: 20 additions & 0 deletions examples/embeddable_examples/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* 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.
*/

export { TodoSavedObjectAttributes } from './todo_saved_object_attributes';
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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 '../../../src/core/types';

export interface TodoSavedObjectAttributes extends SavedObjectAttributes {
task: string;
icon?: string;
title?: string;
}
2 changes: 1 addition & 1 deletion examples/embeddable_examples/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.0.1",
"kibanaVersion": "kibana",
"configPath": ["embeddable_examples"],
"server": false,
"server": true,
"ui": true,
"requiredPlugins": ["embeddable"],
"optionalPlugins": []
Expand Down
36 changes: 36 additions & 0 deletions examples/embeddable_examples/public/create_sample_data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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 { SavedObjectsClientContract } from 'kibana/public';
import { TodoSavedObjectAttributes } from '../common';

export async function createSampleData(client: SavedObjectsClientContract) {
await client.create<TodoSavedObjectAttributes>(
'todo',
{
task: 'Take the garbage out',
title: 'Garbage',
icon: 'trash',
},
{
id: 'sample-todo-saved-object',
overwrite: true,
}
);
}
15 changes: 2 additions & 13 deletions examples/embeddable_examples/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
* under the License.
*/

import { PluginInitializer } from 'kibana/public';
export {
HELLO_WORLD_EMBEDDABLE,
HelloWorldEmbeddable,
Expand All @@ -26,18 +25,8 @@ export {
export { ListContainer, LIST_CONTAINER } from './list_container';
export { TODO_EMBEDDABLE } from './todo';

import {
EmbeddableExamplesPlugin,
EmbeddableExamplesSetupDependencies,
EmbeddableExamplesStartDependencies,
} from './plugin';
import { EmbeddableExamplesPlugin } from './plugin';

export { SearchableListContainer, SEARCHABLE_LIST_CONTAINER } from './searchable_list_container';
export { MULTI_TASK_TODO_EMBEDDABLE } from './multi_task_todo';

export const plugin: PluginInitializer<
void,
void,
EmbeddableExamplesSetupDependencies,
EmbeddableExamplesStartDependencies
> = () => new EmbeddableExamplesPlugin();
export const plugin = () => new EmbeddableExamplesPlugin();
37 changes: 33 additions & 4 deletions examples/embeddable_examples/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,20 @@ import { EmbeddableSetup, EmbeddableStart } from '../../../src/plugins/embeddabl
import { Plugin, CoreSetup, CoreStart } from '../../../src/core/public';
import { HelloWorldEmbeddableFactory, HELLO_WORLD_EMBEDDABLE } from './hello_world';
import { TODO_EMBEDDABLE, TodoEmbeddableFactory, TodoInput, TodoOutput } from './todo';
import { MULTI_TASK_TODO_EMBEDDABLE, MultiTaskTodoEmbeddableFactory } from './multi_task_todo';
import {
MULTI_TASK_TODO_EMBEDDABLE,
MultiTaskTodoEmbeddableFactory,
MultiTaskTodoInput,
MultiTaskTodoOutput,
} from './multi_task_todo';
import {
SEARCHABLE_LIST_CONTAINER,
SearchableListContainerFactory,
} from './searchable_list_container';
import { LIST_CONTAINER, ListContainerFactory } from './list_container';
import { createSampleData } from './create_sample_data';
import { TodoRefInput, TodoRefOutput, TODO_REF_EMBEDDABLE } from './todo/todo_ref_embeddable';
import { TodoRefEmbeddableFactory } from './todo/todo_ref_embeddable_factory';

export interface EmbeddableExamplesSetupDependencies {
embeddable: EmbeddableSetup;
Expand All @@ -36,9 +44,18 @@ export interface EmbeddableExamplesStartDependencies {
embeddable: EmbeddableStart;
}

export interface EmbeddableExamplesStart {
createSampleData: () => Promise<void>;
}

export class EmbeddableExamplesPlugin
implements
Plugin<void, void, EmbeddableExamplesSetupDependencies, EmbeddableExamplesStartDependencies> {
Plugin<
void,
EmbeddableExamplesStart,
EmbeddableExamplesSetupDependencies,
EmbeddableExamplesStartDependencies
> {
public setup(
core: CoreSetup<EmbeddableExamplesStartDependencies>,
deps: EmbeddableExamplesSetupDependencies
Expand All @@ -48,7 +65,7 @@ export class EmbeddableExamplesPlugin
new HelloWorldEmbeddableFactory()
);

deps.embeddable.registerEmbeddableFactory(
deps.embeddable.registerEmbeddableFactory<MultiTaskTodoInput, MultiTaskTodoOutput>(
MULTI_TASK_TODO_EMBEDDABLE,
new MultiTaskTodoEmbeddableFactory()
);
Expand All @@ -73,9 +90,21 @@ export class EmbeddableExamplesPlugin
openModal: (await core.getStartServices())[0].overlays.openModal,
}))
);

deps.embeddable.registerEmbeddableFactory<TodoRefInput, TodoRefOutput>(
TODO_REF_EMBEDDABLE,
new TodoRefEmbeddableFactory(async () => ({
savedObjectsClient: (await core.getStartServices())[0].savedObjects.client,
getEmbeddableFactory: (await core.getStartServices())[1].embeddable.getEmbeddableFactory,
}))
);
}

public start(core: CoreStart, deps: EmbeddableExamplesStartDependencies) {}
public start(core: CoreStart, deps: EmbeddableExamplesStartDependencies) {
return {
createSampleData: () => createSampleData(core.savedObjects.client),
};
}

public stop() {}
}
43 changes: 43 additions & 0 deletions examples/embeddable_examples/public/todo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
There are two examples in here:
- TodoEmbeddable
- TodoRefEmbeddable

# TodoEmbeddable

The first example you should review is the HelloWorldEmbeddable. That is as basic an embeddable as you can get.
This embeddable is the next step up - an embeddable that renders dynamic input data. The data is simple:
- a required task string
- an optional title
- an optional icon string
- an optional search string

It also has output data, which is `hasMatch` - whether or not the search string has matched any input data.

`hasMatch` is a better fit for output data than input data, because it's state that is _derived_ from input data.

For example, if it was input data, you could create a TodoEmbeddable with input like this:

```ts
todoEmbeddableFactory.create({ task: 'take out the garabage', search: 'garbage', hasMatch: false });
```

That's wrong because there is actually a match from the search string inside the task.

The TodoEmbeddable component itself doesn't do anything with the `hasMatch` variable other than set it, but
if you check out `SearchableListContainer`, you can see an example where this output data is being used.

## TodoRefEmbeddable

This is an example of an embeddable based off of a saved object. The input is just the `savedObjectId` and
the `search` string. It has even more output parameters, and this time, it does read it's own output parameters in
order to calculate `hasMatch`.

Output:
```ts
{
hasMatch: boolean,
savedAttributes?: TodoSavedAttributes
}
```

`savedAttributes` is optional because it's possible a TodoSavedObject could not be found with the given savedObjectId.
86 changes: 86 additions & 0 deletions examples/embeddable_examples/public/todo/todo_ref_component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* 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 from 'react';
import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui';

import { EuiText } from '@elastic/eui';
import { EuiAvatar } from '@elastic/eui';
import { EuiIcon } from '@elastic/eui';
import { EuiFlexGrid } from '@elastic/eui';
import { withEmbeddableSubscription } from '../../../../src/plugins/embeddable/public';
import { TodoRefInput, TodoRefOutput, TodoRefEmbeddable } from './todo_ref_embeddable';

interface Props {
embeddable: TodoRefEmbeddable;
input: TodoRefInput;
output: TodoRefOutput;
}

function wrapSearchTerms(task?: string, search?: string) {
if (!search) return task;
if (!task) return task;
const parts = task.split(new RegExp(`(${search})`, 'g'));
return parts.map((part, i) =>
part === search ? (
<span key={i} style={{ backgroundColor: 'yellow' }}>
{part}
</span>
) : (
part
)
);
}

export function TodoRefEmbeddableComponentInner({
input: { search },
output: { savedAttributes },
}: Props) {
const icon = savedAttributes?.icon;
const title = savedAttributes?.title;
const task = savedAttributes?.task;
return (
<EuiFlexGroup>
<EuiFlexItem grow={false}>
{icon ? (
<EuiIcon type={icon} size="l" />
) : (
<EuiAvatar name={title || task || ''} size="l" />
)}
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexGrid columns={1}>
<EuiFlexItem>
<EuiText data-test-subj="todoEmbeddableTitle">
<h3>{wrapSearchTerms(title || '', search)}</h3>
</EuiText>
</EuiFlexItem>
<EuiFlexItem>
<EuiText data-test-subj="todoEmbeddableTask">{wrapSearchTerms(task, search)}</EuiText>
</EuiFlexItem>
</EuiFlexGrid>
</EuiFlexItem>
</EuiFlexGroup>
);
}

export const TodoRefEmbeddableComponent = withEmbeddableSubscription<
TodoRefInput,
TodoRefOutput,
TodoRefEmbeddable
>(TodoRefEmbeddableComponentInner);
Loading

0 comments on commit d040448

Please sign in to comment.