Skip to content

Commit 5a74919

Browse files
cnasikasStacey Gammon
authored andcommitted
Add embeddable via saved object example
1 parent 29abe5b commit 5a74919

File tree

31 files changed

+679
-90
lines changed

31 files changed

+679
-90
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
export { TodoSavedObjectAttributes } from './todo_saved_object_attributes';
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import { SavedObjectAttributes } from '../../../src/core/types';
21+
22+
export interface TodoSavedObjectAttributes extends SavedObjectAttributes {
23+
task: string;
24+
icon?: string;
25+
title?: string;
26+
}

examples/embeddable_examples/kibana.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "0.0.1",
44
"kibanaVersion": "kibana",
55
"configPath": ["embeddable_examples"],
6-
"server": false,
6+
"server": true,
77
"ui": true,
88
"requiredPlugins": ["embeddable"],
99
"optionalPlugins": []
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import { SavedObjectsClientContract } from 'kibana/public';
21+
import { TodoSavedObjectAttributes } from '../common';
22+
23+
export async function createSampleData(client: SavedObjectsClientContract) {
24+
await client.create<TodoSavedObjectAttributes>(
25+
'todo',
26+
{
27+
task: 'Take the garbage out',
28+
title: 'Garbage',
29+
icon: 'trash',
30+
},
31+
{
32+
id: 'sample-todo-saved-object',
33+
overwrite: true,
34+
}
35+
);
36+
}

examples/embeddable_examples/public/index.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
* under the License.
1818
*/
1919

20-
import { PluginInitializer } from 'kibana/public';
2120
export {
2221
HELLO_WORLD_EMBEDDABLE,
2322
HelloWorldEmbeddable,
@@ -26,18 +25,8 @@ export {
2625
export { ListContainer, LIST_CONTAINER } from './list_container';
2726
export { TODO_EMBEDDABLE } from './todo';
2827

29-
import {
30-
EmbeddableExamplesPlugin,
31-
EmbeddableExamplesSetupDependencies,
32-
EmbeddableExamplesStartDependencies,
33-
} from './plugin';
28+
import { EmbeddableExamplesPlugin } from './plugin';
3429

3530
export { SearchableListContainer, SEARCHABLE_LIST_CONTAINER } from './searchable_list_container';
3631
export { MULTI_TASK_TODO_EMBEDDABLE } from './multi_task_todo';
37-
38-
export const plugin: PluginInitializer<
39-
void,
40-
void,
41-
EmbeddableExamplesSetupDependencies,
42-
EmbeddableExamplesStartDependencies
43-
> = () => new EmbeddableExamplesPlugin();
32+
export const plugin = () => new EmbeddableExamplesPlugin();

examples/embeddable_examples/public/plugin.ts

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,20 @@ import { EmbeddableSetup, EmbeddableStart } from '../../../src/plugins/embeddabl
2121
import { Plugin, CoreSetup, CoreStart } from '../../../src/core/public';
2222
import { HelloWorldEmbeddableFactory, HELLO_WORLD_EMBEDDABLE } from './hello_world';
2323
import { TODO_EMBEDDABLE, TodoEmbeddableFactory, TodoInput, TodoOutput } from './todo';
24-
import { MULTI_TASK_TODO_EMBEDDABLE, MultiTaskTodoEmbeddableFactory } from './multi_task_todo';
24+
import {
25+
MULTI_TASK_TODO_EMBEDDABLE,
26+
MultiTaskTodoEmbeddableFactory,
27+
MultiTaskTodoInput,
28+
MultiTaskTodoOutput,
29+
} from './multi_task_todo';
2530
import {
2631
SEARCHABLE_LIST_CONTAINER,
2732
SearchableListContainerFactory,
2833
} from './searchable_list_container';
2934
import { LIST_CONTAINER, ListContainerFactory } from './list_container';
35+
import { createSampleData } from './create_sample_data';
36+
import { TodoRefInput, TodoRefOutput, TODO_REF_EMBEDDABLE } from './todo/todo_ref_embeddable';
37+
import { TodoRefEmbeddableFactory } from './todo/todo_ref_embeddable_factory';
3038

3139
export interface EmbeddableExamplesSetupDependencies {
3240
embeddable: EmbeddableSetup;
@@ -36,9 +44,18 @@ export interface EmbeddableExamplesStartDependencies {
3644
embeddable: EmbeddableStart;
3745
}
3846

47+
export interface EmbeddableExamplesStart {
48+
createSampleData: () => Promise<void>;
49+
}
50+
3951
export class EmbeddableExamplesPlugin
4052
implements
41-
Plugin<void, void, EmbeddableExamplesSetupDependencies, EmbeddableExamplesStartDependencies> {
53+
Plugin<
54+
void,
55+
EmbeddableExamplesStart,
56+
EmbeddableExamplesSetupDependencies,
57+
EmbeddableExamplesStartDependencies
58+
> {
4259
public setup(
4360
core: CoreSetup<EmbeddableExamplesStartDependencies>,
4461
deps: EmbeddableExamplesSetupDependencies
@@ -48,7 +65,7 @@ export class EmbeddableExamplesPlugin
4865
new HelloWorldEmbeddableFactory()
4966
);
5067

51-
deps.embeddable.registerEmbeddableFactory(
68+
deps.embeddable.registerEmbeddableFactory<MultiTaskTodoInput, MultiTaskTodoOutput>(
5269
MULTI_TASK_TODO_EMBEDDABLE,
5370
new MultiTaskTodoEmbeddableFactory()
5471
);
@@ -73,9 +90,21 @@ export class EmbeddableExamplesPlugin
7390
openModal: (await core.getStartServices())[0].overlays.openModal,
7491
}))
7592
);
93+
94+
deps.embeddable.registerEmbeddableFactory<TodoRefInput, TodoRefOutput>(
95+
TODO_REF_EMBEDDABLE,
96+
new TodoRefEmbeddableFactory(async () => ({
97+
savedObjectsClient: (await core.getStartServices())[0].savedObjects.client,
98+
getEmbeddableFactory: (await core.getStartServices())[1].embeddable.getEmbeddableFactory,
99+
}))
100+
);
76101
}
77102

78-
public start(core: CoreStart, deps: EmbeddableExamplesStartDependencies) {}
103+
public start(core: CoreStart, deps: EmbeddableExamplesStartDependencies) {
104+
return {
105+
createSampleData: () => createSampleData(core.savedObjects.client),
106+
};
107+
}
79108

80109
public stop() {}
81110
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
There are two examples in here:
2+
- TodoEmbeddable
3+
- TodoRefEmbeddable
4+
5+
# TodoEmbeddable
6+
7+
The first example you should review is the HelloWorldEmbeddable. That is as basic an embeddable as you can get.
8+
This embeddable is the next step up - an embeddable that renders dynamic input data. The data is simple:
9+
- a required task string
10+
- an optional title
11+
- an optional icon string
12+
- an optional search string
13+
14+
It also has output data, which is `hasMatch` - whether or not the search string has matched any input data.
15+
16+
`hasMatch` is a better fit for output data than input data, because it's state that is _derived_ from input data.
17+
18+
For example, if it was input data, you could create a TodoEmbeddable with input like this:
19+
20+
```ts
21+
todoEmbeddableFactory.create({ task: 'take out the garabage', search: 'garbage', hasMatch: false });
22+
```
23+
24+
That's wrong because there is actually a match from the search string inside the task.
25+
26+
The TodoEmbeddable component itself doesn't do anything with the `hasMatch` variable other than set it, but
27+
if you check out `SearchableListContainer`, you can see an example where this output data is being used.
28+
29+
## TodoRefEmbeddable
30+
31+
This is an example of an embeddable based off of a saved object. The input is just the `savedObjectId` and
32+
the `search` string. It has even more output parameters, and this time, it does read it's own output parameters in
33+
order to calculate `hasMatch`.
34+
35+
Output:
36+
```ts
37+
{
38+
hasMatch: boolean,
39+
savedAttributes?: TodoSavedAttributes
40+
}
41+
```
42+
43+
`savedAttributes` is optional because it's possible a TodoSavedObject could not be found with the given savedObjectId.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
import React from 'react';
20+
import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
21+
22+
import { EuiText } from '@elastic/eui';
23+
import { EuiAvatar } from '@elastic/eui';
24+
import { EuiIcon } from '@elastic/eui';
25+
import { EuiFlexGrid } from '@elastic/eui';
26+
import { withEmbeddableSubscription } from '../../../../src/plugins/embeddable/public';
27+
import { TodoRefInput, TodoRefOutput, TodoRefEmbeddable } from './todo_ref_embeddable';
28+
29+
interface Props {
30+
embeddable: TodoRefEmbeddable;
31+
input: TodoRefInput;
32+
output: TodoRefOutput;
33+
}
34+
35+
function wrapSearchTerms(task?: string, search?: string) {
36+
if (!search) return task;
37+
if (!task) return task;
38+
const parts = task.split(new RegExp(`(${search})`, 'g'));
39+
return parts.map((part, i) =>
40+
part === search ? (
41+
<span key={i} style={{ backgroundColor: 'yellow' }}>
42+
{part}
43+
</span>
44+
) : (
45+
part
46+
)
47+
);
48+
}
49+
50+
export function TodoRefEmbeddableComponentInner({
51+
input: { search },
52+
output: { savedAttributes },
53+
}: Props) {
54+
const icon = savedAttributes?.icon;
55+
const title = savedAttributes?.title;
56+
const task = savedAttributes?.task;
57+
return (
58+
<EuiFlexGroup>
59+
<EuiFlexItem grow={false}>
60+
{icon ? (
61+
<EuiIcon type={icon} size="l" />
62+
) : (
63+
<EuiAvatar name={title || task || ''} size="l" />
64+
)}
65+
</EuiFlexItem>
66+
<EuiFlexItem>
67+
<EuiFlexGrid columns={1}>
68+
<EuiFlexItem>
69+
<EuiText data-test-subj="todoEmbeddableTitle">
70+
<h3>{wrapSearchTerms(title || '', search)}</h3>
71+
</EuiText>
72+
</EuiFlexItem>
73+
<EuiFlexItem>
74+
<EuiText data-test-subj="todoEmbeddableTask">{wrapSearchTerms(task, search)}</EuiText>
75+
</EuiFlexItem>
76+
</EuiFlexGrid>
77+
</EuiFlexItem>
78+
</EuiFlexGroup>
79+
);
80+
}
81+
82+
export const TodoRefEmbeddableComponent = withEmbeddableSubscription(
83+
TodoRefEmbeddableComponentInner
84+
);

0 commit comments

Comments
 (0)