Skip to content

Commit

Permalink
Embeddable examples on the platform and included with `--run-examples…
Browse files Browse the repository at this point in the history
…` flag (elastic#52111)

* Add a new platform embeddable example plugin

* Remove extra hello world test impl.

* cleanup

* code review updates

* Change example to highlight and have parent filter out children

* Fix deep comparison of embeddable prop

* adjust help text
  • Loading branch information
stacey-gammon committed Dec 16, 2019
1 parent 3d4f1f0 commit abdb7a5
Show file tree
Hide file tree
Showing 69 changed files with 2,478 additions and 310 deletions.
1 change: 1 addition & 0 deletions .i18nrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"src/plugins/data"
],
"embeddableApi": "src/plugins/embeddable",
"embeddableExamples": "examples/embeddable_examples",
"share": "src/plugins/share",
"esUi": "src/plugins/es_ui_shared",
"devTools": "src/plugins/dev_tools",
Expand Down
4 changes: 2 additions & 2 deletions examples/demo_search/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "demo_data_search",
"version": "1.0.0",
"main": "target/test/plugin_functional/plugins/demo_data_search",
"main": "target/examples/demo_data_search",
"kibana": {
"version": "kibana",
"templateVersion": "1.0.0"
Expand All @@ -12,6 +12,6 @@
"build": "rm -rf './target' && tsc"
},
"devDependencies": {
"typescript": "3.5.3"
"typescript": "3.7.2"
}
}
10 changes: 10 additions & 0 deletions examples/embeddable_examples/kibana.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"id": "embeddableExamples",
"version": "0.0.1",
"kibanaVersion": "kibana",
"configPath": ["embeddable_examples"],
"server": false,
"ui": true,
"requiredPlugins": ["embeddable"],
"optionalPlugins": []
}
17 changes: 17 additions & 0 deletions examples/embeddable_examples/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "embeddable_examples",
"version": "1.0.0",
"main": "target/examples/embeddable_examples",
"kibana": {
"version": "kibana",
"templateVersion": "1.0.0"
},
"license": "Apache-2.0",
"scripts": {
"kbn": "node ../../scripts/kbn.js",
"build": "rm -rf './target' && tsc"
},
"devDependencies": {
"typescript": "3.7.2"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
import { Embeddable, EmbeddableInput, IContainer } from '../../..';
import { Embeddable, EmbeddableInput, IContainer } from '../../../../src/plugins/embeddable/public';

export const HELLO_WORLD_EMBEDDABLE_TYPE = 'HELLO_WORLD_EMBEDDABLE_TYPE';
export const HELLO_WORLD_EMBEDDABLE = 'HELLO_WORLD_EMBEDDABLE';

export class HelloWorldEmbeddable extends Embeddable {
// The type of this embeddable. This will be used to find the appropriate factory
// to instantiate this kind of embeddable.
public readonly type = HELLO_WORLD_EMBEDDABLE_TYPE;
public readonly type = HELLO_WORLD_EMBEDDABLE;

constructor(initialInput: EmbeddableInput, parent?: IContainer) {
super(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@
*/

import { i18n } from '@kbn/i18n';
import { IContainer, EmbeddableInput, EmbeddableFactory } from '../../..';
import { HelloWorldEmbeddable, HELLO_WORLD_EMBEDDABLE_TYPE } from './hello_world_embeddable';
import {
IContainer,
EmbeddableInput,
EmbeddableFactory,
} from '../../../../src/plugins/embeddable/public';
import { HelloWorldEmbeddable, HELLO_WORLD_EMBEDDABLE } from './hello_world_embeddable';

export class HelloWorldEmbeddableFactory extends EmbeddableFactory {
public readonly type = HELLO_WORLD_EMBEDDABLE_TYPE;
public readonly type = HELLO_WORLD_EMBEDDABLE;

/**
* In our simple example, we let everyone have permissions to edit this. Most
Expand All @@ -38,7 +42,7 @@ export class HelloWorldEmbeddableFactory extends EmbeddableFactory {
}

public getDisplayName() {
return i18n.translate('embeddableApi.samples.helloworld.displayName', {
return i18n.translate('embeddableExamples.helloworld.displayName', {
defaultMessage: 'hello world',
});
}
Expand Down
34 changes: 34 additions & 0 deletions examples/embeddable_examples/public/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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 { PluginInitializer } from 'kibana/public';
export {
HELLO_WORLD_EMBEDDABLE,
HelloWorldEmbeddable,
HelloWorldEmbeddableFactory,
} from './hello_world';
export { ListContainer, LIST_CONTAINER } from './list_container';
export { TODO_EMBEDDABLE } from './todo';

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> = () => new EmbeddableExamplesPlugin();
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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 { EuiPanel, EuiLoadingSpinner, EuiFlexItem } from '@elastic/eui';
import { IEmbeddable } from '../../../../src/plugins/embeddable/public';

interface Props {
embeddable: IEmbeddable;
}

export class EmbeddableListItem extends React.Component<Props> {
private embeddableRoot: React.RefObject<HTMLDivElement>;
private rendered = false;

constructor(props: Props) {
super(props);
this.embeddableRoot = React.createRef();
}

public componentDidMount() {
if (this.embeddableRoot.current && this.props.embeddable) {
this.props.embeddable.render(this.embeddableRoot.current);
this.rendered = true;
}
}

public componentDidUpdate() {
if (this.embeddableRoot.current && this.props.embeddable && !this.rendered) {
this.props.embeddable.render(this.embeddableRoot.current);
this.rendered = true;
}
}

public render() {
return (
<EuiFlexItem>
<EuiPanel>
{this.props.embeddable ? (
<div ref={this.embeddableRoot} />
) : (
<EuiLoadingSpinner size="s" />
)}
</EuiPanel>
</EuiFlexItem>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,5 @@
* under the License.
*/

import expect from '@kbn/expect';

export default function({ getService }) {
const testSubjects = getService('testSubjects');
const retry = getService('retry');

describe('hello world container', () => {
before(async () => {
await testSubjects.click('embedExplorerTab-helloWorldContainer');
});

it('hello world embeddable renders', async () => {
await retry.try(async () => {
const text = await testSubjects.getVisibleText('helloWorldEmbeddable');
expect(text).to.be('HELLO WORLD!');
});
});
});
}
export { ListContainer, LIST_CONTAINER } from './list_container';
export { ListContainerFactory } from './list_container_factory';
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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 ReactDOM from 'react-dom';
import {
Container,
ContainerInput,
GetEmbeddableFactory,
} from '../../../../src/plugins/embeddable/public';
import { ListContainerComponent } from './list_container_component';

export const LIST_CONTAINER = 'LIST_CONTAINER';

export class ListContainer extends Container<{}, ContainerInput> {
public readonly type = LIST_CONTAINER;
private node?: HTMLElement;

constructor(input: ContainerInput, getEmbeddableFactory: GetEmbeddableFactory) {
super(input, { embeddableLoaded: {} }, getEmbeddableFactory);
}

// This container has no input itself.
getInheritedInput(id: string) {
return {};
}

public render(node: HTMLElement) {
this.node = node;
if (this.node) {
ReactDOM.unmountComponentAtNode(this.node);
}
ReactDOM.render(<ListContainerComponent embeddable={this} />, node);
}

public destroy() {
super.destroy();
if (this.node) {
ReactDOM.unmountComponentAtNode(this.node);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* 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 { EuiFlexGroup, EuiSpacer, EuiFlexItem, EuiText, EuiPanel } from '@elastic/eui';
import {
IContainer,
withEmbeddableSubscription,
ContainerInput,
ContainerOutput,
} from '../../../../src/plugins/embeddable/public';
import { EmbeddableListItem } from './embeddable_list_item';

interface Props {
embeddable: IContainer;
input: ContainerInput;
output: ContainerOutput;
}

function renderList(embeddable: IContainer, panels: ContainerInput['panels']) {
let number = 0;
const list = Object.values(panels).map(panel => {
const child = embeddable.getChild(panel.explicitInput.id);
number++;
return (
<EuiPanel key={number.toString()}>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiText>
<h3>{number}</h3>
</EuiText>
</EuiFlexItem>
<EuiFlexItem>
<EmbeddableListItem embeddable={child} />
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
);
});
return list;
}

export function ListContainerComponentInner(props: Props) {
return (
<div>
<h2 data-test-subj="listContainerTitle">{props.embeddable.getTitle()}</h2>
<EuiSpacer size="l" />
{renderList(props.embeddable, props.input.panels)}
</div>
);
}

// You don't have to use this helper wrapper, but it handles a lot of the React boilerplate for
// embeddables, like setting up the subscriptions to cause the component to refresh whenever
// anything on input or output state changes. If you don't want that to happen (for example
// if you expect something on input or output state to change frequently that your react
// component does not care about, then you should probably hook this up manually).
export const ListContainerComponent = withEmbeddableSubscription(ListContainerComponentInner);
Loading

0 comments on commit abdb7a5

Please sign in to comment.