Skip to content

Commit

Permalink
embeddable overview guiding principles and best practices (#183099)
Browse files Browse the repository at this point in the history
Paired with @ThomThomson to expand Embeddable documentation with
"Guiding principles" and "Best practices"

PR also moves overview to src/plugins/embeddables/README.md. Then, this
markdown is displayed in the embeddable example application as well.

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Devon Thomson <devon.thomson@elastic.co>
  • Loading branch information
3 people committed May 14, 2024
1 parent 7026199 commit 3ecd73c
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 26 deletions.
3 changes: 1 addition & 2 deletions dev_docs/key_concepts/embeddables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,4 @@ date: 2024-05-09
tags: ['kibana', 'dev', 'contributor', 'api docs']
---

Embeddable documentation and examples are bundled with Kibana and accessable by running `yarn start --run-examples`.
Navigate to `http://localhost:5601/app/embeddablesApp`.
Embeddable documentation available at [/src/plugins/embeddable/README.md](https://github.com/elastic/kibana/blob/main/src/plugins/embeddable/README.md)
3 changes: 1 addition & 2 deletions docs/developer/plugin-list.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,7 @@ This API doesn't support angular, for registering angular dev tools, bootstrap a
|{kib-repo}blob/{branch}/src/plugins/embeddable/README.md[embeddable]
|Embeddable documentation and examples are bundled with Kibana and accessable by running yarn start --run-examples.
Navigate to http://localhost:5601/app/embeddablesApp.
|Embeddables are React components that manage their own state, can be serialized and deserialized, and return an API that can be used to interact with them imperatively.
|{kib-repo}blob/{branch}/src/plugins/es_ui_shared/README.md[esUiShared]
Expand Down
19 changes: 11 additions & 8 deletions examples/embeddable_examples/public/app/overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@
*/

import React from 'react';

import { EuiText } from '@elastic/eui';
import { css } from '@emotion/react';
import { EuiMarkdownFormat } from '@elastic/eui';
// @ts-ignore
import overviewMarkdown from '!!raw-loader!@kbn/embeddable-plugin/README.md';

export const Overview = () => {
return (
<EuiText>
<p>
Embeddables are React components that manage their own state, can be serialized and
deserialized, and return an API that can be used to interact with them imperatively.
</p>
</EuiText>
<EuiMarkdownFormat
css={css`
width: 75%;
`}
>
{overviewMarkdown}
</EuiMarkdownFormat>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ export const RegisterEmbeddable = () => {
<h2>Register a new embeddable type</h2>
<p>
This plugin registers several embeddable types with{' '}
<strong>registerReactEmbeddableFactory</strong> during plugin start. The code example
below shows Search embeddable registration. Notice how the embeddable factory is imported
asynchronously to limit initial page load size.
<strong>registerReactEmbeddableFactory</strong>. The code example below shows Search
embeddable registration. The embeddable factory is imported asynchronously to limit
initial page load size.
</p>
</EuiText>
<EuiSpacer size="s" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,11 @@ export function SearchEmbeddableRenderer(props: Props) {
}, [props.timeRange, parentApi.timeRange$]);

return (
<div className="mapEmbeddableContainer">
<ReactEmbeddableRenderer<SearchSerializedState, SearchApi>
type={SEARCH_EMBEDDABLE_ID}
state={initialState}
parentApi={parentApi}
hidePanelChrome={true}
/>
</div>
<ReactEmbeddableRenderer<SearchSerializedState, SearchApi>
type={SEARCH_EMBEDDABLE_ID}
state={initialState}
parentApi={parentApi}
hidePanelChrome={true}
/>
);
}
23 changes: 20 additions & 3 deletions src/plugins/embeddable/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
# Documentation and examples
Embeddables are React components that manage their own state, can be serialized and deserialized, and return an API that can be used to interact with them imperatively.

Embeddable documentation and examples are bundled with Kibana and accessable by running `yarn start --run-examples`.
Navigate to `http://localhost:5601/app/embeddablesApp`.
#### Guiding principles
* **Coupled to React:** Kibana is a React application, and the minimum unit of sharing is the React component. Embeddables enforce this by requiring a React component during registration.
* **Composition over inheritence:** Rather than an inheritance-based system with classes, imperative APIs are plain old typescript objects that implement any number of shared interfaces. Interfaces are enforced via type guards and are shared via Packages.
* **Internal state management:** Each embeddable manages its own state. This is because the embeddable system allows a page to render a registry of embeddable types that can change over time. This makes it untenable for a single page to manage state for every type of embeddable. The page is only responsible for persisting and providing the last persisted state to the embeddable on startup.

#### Best practices
* **Do not use Embeddables to share Components between plugins: ** Only create an embeddable if your Component is rendered on a page that persists embeddable state and renders multiple embeddable types. For example, create an embeddable to render your Component on a Dashboard. Otherwise, use a vanilla React Component to share Components between plugins.
* **Do not use Embeddables to avoid circular plugin dependencies: ** Break your Component into a Package or another plugin to avoid circular plugin dependencies.
* **Minimal API surface area: ** Embeddable APIs are accessable to all Kibana systems and all embeddable siblings and parents. Functions and state that are internal to an embeddable including any child components should not be added to the API. Consider passing internal state to child as props or react context.

#### Examples
Examples available at [/examples/embeddable_examples](https://github.com/elastic/kibana/tree/main/examples/embeddable_examples)
* [Register an embeddable](https://github.com/elastic/kibana/blob/main/examples/embeddable_examples/public/react_embeddables/search/register_search_embeddable.ts)
* [Embeddable that responds to Unified search](https://github.com/elastic/kibana/blob/main/examples/embeddable_examples/public/react_embeddables/search/search_react_embeddable.tsx)
* [Embeddable that interacts with sibling embeddables](https://github.com/elastic/kibana/blob/main/examples/embeddable_examples/public/react_embeddables/data_table/data_table_react_embeddable.tsx)
* [Render an embeddable](https://github.com/elastic/kibana/blob/main/examples/embeddable_examples/public/react_embeddables/search/search_embeddable_renderer.tsx)

Run examples with `yarn start --run-examples`
To access example embeddables, create a new dashboard, click "Add panel" and finally select "Embeddable examples".

0 comments on commit 3ecd73c

Please sign in to comment.