mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
adding documentation for visualization development (#14252)
* adding documentation for visualization development
This commit is contained in:
parent
425239ef9e
commit
39b14bed02
5 changed files with 543 additions and 0 deletions
|
@ -9,6 +9,7 @@ The Kibana plugin interfaces are in a state of constant development. We cannot
|
|||
* <<development-plugin-resources>>
|
||||
* <<development-uiexports>>
|
||||
* <<development-plugin-functional-tests>>
|
||||
* <<development-visualize-index>>
|
||||
|
||||
|
||||
include::plugin/development-plugin-resources.asciidoc[]
|
||||
|
@ -16,3 +17,5 @@ include::plugin/development-plugin-resources.asciidoc[]
|
|||
include::plugin/development-uiexports.asciidoc[]
|
||||
|
||||
include::plugin/development-plugin-functional-tests.asciidoc[]
|
||||
|
||||
include::visualize/development-visualize-index.asciidoc[]
|
|
@ -0,0 +1,413 @@
|
|||
[[development-create-visualization]]
|
||||
=== Developing Visualizations
|
||||
|
||||
This is a short description of functions and interfaces provided. For more information you should check the kibana
|
||||
source code and the existing visualizations provided with it.
|
||||
|
||||
- <<development-visualization-factory>>
|
||||
* <<development-base-visualization-type>>
|
||||
* <<development-angular-visualization-type>>
|
||||
* <<development-react-visualization-type>>
|
||||
* <<development-vislib-visualization-type>>
|
||||
- <<development-vis-editors>>
|
||||
* <<development-default-editor>>
|
||||
* <<development-custom-editor>>
|
||||
- <<development-visualization-request-handlers>>
|
||||
* <<development-default-request-handler>>
|
||||
* <<development-none-request-handler>>
|
||||
* <<development-custom-request-handler>>
|
||||
- <<development-visualization-response-handlers>>
|
||||
* <<development-default-response-handler>>
|
||||
* <<development-none-response-handler>>
|
||||
* <<development-custom-response-handler>>
|
||||
- <<development-vis-object>>
|
||||
* <<development-vis-timefilter>>
|
||||
|
||||
[[development-visualization-factory]]
|
||||
=== Visualization Factory
|
||||
|
||||
Use the `VisualizationFactory` to create a new visualization.
|
||||
The creation-methods create a new visualization tied to the underlying rendering technology.
|
||||
You should also register the visualization with `VisTypesRegistryProvider`.
|
||||
|
||||
["source","html"]
|
||||
-----------
|
||||
import { CATEGORY } from 'ui/vis/vis_category';
|
||||
import { VisFactoryProvider } from 'ui/vis/vis_factory';
|
||||
import { VisTypesRegistryProvider } from 'ui/registry/vis_types';
|
||||
|
||||
const MyNewVisType = (Private) => {
|
||||
const VisFactory = Private(VisFactoryProvider);
|
||||
|
||||
return VisFactory.createBaseVisualization({
|
||||
name: 'my_new_vis',
|
||||
title: 'My New Vis',
|
||||
icon: 'my_icon',
|
||||
description: 'Cool new chart',
|
||||
category: CATEGORY.OTHER
|
||||
...
|
||||
});
|
||||
}
|
||||
|
||||
VisTypesRegistryProvider.register(MyNewVisType);
|
||||
-----------
|
||||
|
||||
The list of common parameters:
|
||||
|
||||
- *name*: unique visualization name, only lowercase letters and underscore
|
||||
- *title*: title of your visualization as displayed in kibana
|
||||
- *icon*: the icon class to use (font awesome)
|
||||
- *image*: instead of icon you can provide svg image (imported)
|
||||
- *description*: description of your visualization as shown in kibana
|
||||
- *category*: the category your visualization falls into (one of `ui/vis/vis_category` values)
|
||||
- *visConfig*: object holding visualization parameters
|
||||
- *visConfig.defaults*: object holding default visualization configuration
|
||||
- *visualization*: A constructor function for a Visualization.
|
||||
- *requestHandler*: <string> one of the available request handlers or a <function> for a custom request handler
|
||||
- *responseHandler*: <string> one of the available response handlers or a <function> for a custom response handler
|
||||
- *editor*: <string> one of the available editors or Editor class for custom one
|
||||
- *editorConfig*: object holding editor parameters
|
||||
- *options.showTimePicker*: <bool> show or hide time picker (defaults to true)
|
||||
- *options.showQueryBar*: <bool> show or hide query bar (defaults to true)
|
||||
- *options.showFilterBar*: <bool> show or hide filter bar (defaults to true)
|
||||
- *options.showIndexSelection*: <bool> show or hide index selection (defaults to true)
|
||||
- *isExperimental*: <bool> mark visualization as experimental (defaults to false)
|
||||
|
||||
|
||||
Each of the factories have some of the custom parameters, which will be described below.
|
||||
|
||||
[[development-base-visualization-type]]
|
||||
==== Base Visualization Type
|
||||
The base visualization type does not make any assumptions about the rendering technology you are going to use and
|
||||
works with pure Javascript. It is the visualization type we recommend to use.
|
||||
|
||||
You need to provide a type with a constructor function, a render method which will be called every time
|
||||
options or data change, and a destroy method which will be called to cleanup.
|
||||
|
||||
The render function receives the data object and status object which tells what actually changed.
|
||||
Render function needs to return a promise, which should be resolved once the visualization is done rendering.
|
||||
|
||||
Status object has the following properties: `vis`, `aggs`, `resize`, `data`. Each of them is set to true if the matching
|
||||
object changed since last call to the render function or set to false otherwise. You can use it to make your
|
||||
visualization rendering more efficient.
|
||||
|
||||
|
||||
image::images/visualize-flow.png[Main Flow]
|
||||
|
||||
- Your visualizations constructor will get called with `vis` object and the DOM-element to which it should render.
|
||||
At this point you should prepare everything for rendering, but not render yet
|
||||
- `<visualize>` component monitors `appState`, `uiState` and `vis` for changes
|
||||
- on changes the `<visualize>`-directive will call your `requestHandler`.
|
||||
Implementing a request handler is optional, as you might use one of the provided ones.
|
||||
- response from `requestHandler` will get passed to `responseHandler`. It should convert raw data to something that
|
||||
can be consumed by visualization. Implementing `responseHandler` is optional, as you might use of of the provided ones.
|
||||
- On new data from the `responseHandler` or on when the size of the surrounding DOM-element has changed,
|
||||
your visualization `render`-method gets called. It needs to return a promise which resolves once the visualization
|
||||
is done rendering.
|
||||
- the visualization should call `vis.updateState()` any time something has changed that requires to
|
||||
re-render or fetch new data.
|
||||
|
||||
["source","js"]
|
||||
-----------
|
||||
import { VisFactoryProvider } from 'ui/vis/vis_factory';
|
||||
import { VisTypesRegistryProvider } from 'ui/registry/vis_types';
|
||||
|
||||
class MyVisualization {
|
||||
constructor(vis, el) {
|
||||
this.el = el;
|
||||
this.vis = vis;
|
||||
}
|
||||
render(visData, status) {
|
||||
return new Promise(resolve => {
|
||||
...
|
||||
resolve('done rendering');
|
||||
}),
|
||||
destroy() {
|
||||
console.log('destroying');
|
||||
}
|
||||
}
|
||||
|
||||
const MyNewVisType = (Private) => {
|
||||
const VisFactory = Private(VisFactoryProvider);
|
||||
|
||||
return VisFactory.createBaseVisualization({
|
||||
name: 'my_new_vis',
|
||||
title: 'My New Vis',
|
||||
icon: 'my_icon',
|
||||
description: 'Cool new chart',
|
||||
visualization: MyVisualization
|
||||
});
|
||||
}
|
||||
|
||||
VisTypesRegistryProvider.register(MyNewVisType);
|
||||
-----------
|
||||
|
||||
[[development-angular-visualization-type]]
|
||||
==== AngularJS Visualization Type
|
||||
The AngularJS visualization type assumes you are using angular as your rendering technology. Instead of providing the
|
||||
controller we need to provide the angular template to render.
|
||||
|
||||
The visualization will receive `vis`, `uiState` and `visData` on the $scope and needs to
|
||||
call `$scope.renderComplete()` once it is done rendering.
|
||||
|
||||
["source","js"]
|
||||
-----------
|
||||
const MyNewVisType = (Private) => {
|
||||
const VisFactory = Private(VisFactoryProvider);
|
||||
|
||||
return VisFactory.createAngularVisualization({
|
||||
name: 'my_new_vis',
|
||||
title: 'My New Vis',
|
||||
icon: 'my_icon',
|
||||
description: 'Cool new chart',
|
||||
visConfig: {
|
||||
template: '<div ng-controller="MyAngularController"></div>`
|
||||
}
|
||||
});
|
||||
}
|
||||
-----------
|
||||
|
||||
[[development-react-visualization-type]]
|
||||
==== React Visualization Type
|
||||
React visualization type assumes you are using React as your rendering technology. Instead of passing it an AngularJS
|
||||
template you need to pass a React component.
|
||||
|
||||
The visualization will receive `vis`, `uiState` and `visData` as props.
|
||||
It also has a `renderComplete` function, which needs to be called once the rendering has completed.
|
||||
|
||||
["source","js"]
|
||||
-----------
|
||||
import { ReactComponent } from './my_react_component';
|
||||
|
||||
const MyNewVisType = (Private) => {
|
||||
const VisFactory = Private(VisFactoryProvider);
|
||||
|
||||
return VisFactory.createReactVisualization({
|
||||
name: 'my_new_vis',
|
||||
title: 'My New Vis',
|
||||
icon: 'my_icon',
|
||||
description: 'Cool new chart',
|
||||
visConfig: {
|
||||
template: ReactComponent
|
||||
}
|
||||
});
|
||||
}
|
||||
-----------
|
||||
|
||||
[[development-vislib-visualization-type]]
|
||||
==== Vislib Visualization Type
|
||||
This visualization type should only be used for `vislib` visualizations. Vislib is kibana's D3 library which can produce
|
||||
point series charts and pie charts.
|
||||
|
||||
[[development-vis-editors]]
|
||||
=== Visualization Editors
|
||||
By default, visualizations will use the `default` editor.
|
||||
This is the sidebar editor you see in many of the Kibana visualizations. You can also write your own editor.
|
||||
|
||||
[[development-default-editor]]
|
||||
==== `default` editor controller
|
||||
The default editor controller receives an `optionsTemplate` or `optionsTabs` parameter.
|
||||
These can be either an AngularJS template or React component.
|
||||
|
||||
["source","js"]
|
||||
-----------
|
||||
{
|
||||
name: 'my_new_vis',
|
||||
title: 'My New Vis',
|
||||
icon: 'my_icon',
|
||||
description: 'Cool new chart',
|
||||
editorController: 'default',
|
||||
editorConfig: {
|
||||
optionsTemplate: '<my-custom-options-directive></my-custom-options-directive>' // or
|
||||
optionsTemplate: MyReactComponent // or if multiple tabs are required:
|
||||
optionsTabs: [
|
||||
{ title: 'tab 1', template: '<div>....</div> },
|
||||
{ title: 'tab 2', template: '<my-custom-options-directive></my-custom-options-directive>' },
|
||||
{ title: 'tab 3', template: MyReactComponent }
|
||||
]
|
||||
}
|
||||
}
|
||||
-----------
|
||||
|
||||
[[development-custom-editor]]
|
||||
==== custom editor controller
|
||||
You can create a custom editor controller. To do so pass an Editor object (the same format as VisController class).
|
||||
You can make your controller take extra configuration which is passed to the editorConfig property.
|
||||
|
||||
["source","js"]
|
||||
-----------
|
||||
import { VisFactoryProvider } from 'ui/vis/vis_factory';
|
||||
|
||||
class MyEditorController {
|
||||
constructor(el, vis) {
|
||||
this.el = el;
|
||||
this.vis = vis;
|
||||
this.config = vis.type.editorConfig;
|
||||
}
|
||||
async render(visData) {
|
||||
return new Promise(resolve => {
|
||||
console.log(this.config.my);
|
||||
...
|
||||
resolve('done rendering');
|
||||
}),
|
||||
destroy() {
|
||||
console.log('destroying');
|
||||
}
|
||||
}
|
||||
|
||||
const MyNewVisType = (Private) => {
|
||||
const VisFactory = Private(VisFactoryProvider);
|
||||
|
||||
return VisFactory.createAngularVisualization({
|
||||
name: 'my_new_vis',
|
||||
title: 'My New Vis',
|
||||
icon: 'my_icon',
|
||||
description: 'Cool new chart',
|
||||
editorController: MyEditorController,
|
||||
editorConfig: { my: 'custom config' }
|
||||
});
|
||||
}
|
||||
|
||||
VisTypesRegistryProvider.register(MyNewVisType);
|
||||
-----------
|
||||
|
||||
[[development-visualization-request-handlers]]
|
||||
=== Visualization Request Handlers
|
||||
Request handler gets called when one of the following keys on AppState change:
|
||||
`vis`, `query`, `filters` or `uiState` and when timepicker is updated. On top
|
||||
of that it will also get called on force refresh.
|
||||
|
||||
By default visualizations will use the `courier` request handler. They can also choose to use any of the other provided
|
||||
request handlers. It is also possible to define your own request handler
|
||||
(which you can then register to be used by other visualizations).
|
||||
|
||||
[[development-default-request-handler]]
|
||||
==== courier request handler
|
||||
'courier' is the default request handler which works with the 'default' side bar editor.
|
||||
|
||||
[[development-none-request-handler]]
|
||||
==== `none` request handler
|
||||
Using 'none' as your request handles means your visualization does not require any data to be requested.
|
||||
|
||||
[[development-custom-request-handler]]
|
||||
==== custom request handler
|
||||
You can define your custom request handler by providing a function with the following definition:
|
||||
`function (vis, appState, uiState, searchSource) { ... }`
|
||||
|
||||
This function must return a promise, which should get resolved with new data that will be passed to responseHandler.
|
||||
|
||||
It's up to function to decide when it wants to issue a new request or return previous data
|
||||
(if none of the objects relevant to the request handler changed).
|
||||
|
||||
["source","js"]
|
||||
-----------
|
||||
import { VisFactoryProvider } from 'ui/vis/vis_factory';
|
||||
|
||||
const myRequestHandler = (vis, appState, uiState, searchSource) => {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const data = ... parse ...
|
||||
resolve(data);
|
||||
});
|
||||
};
|
||||
|
||||
const MyNewVisType = (Private) => {
|
||||
const VisFactory = Private(VisFactoryProvider);
|
||||
|
||||
return VisFactory.createAngularVisualization({
|
||||
name: 'my_new_vis',
|
||||
title: 'My New Vis',
|
||||
icon: 'my_icon',
|
||||
description: 'Cool new chart',
|
||||
requestHandler: myRequestHandler
|
||||
});
|
||||
}
|
||||
|
||||
VisTypesRegistryProvider.register(MyNewVisType);
|
||||
-----------
|
||||
|
||||
[[development-visualization-response-handlers]]
|
||||
=== Visualization Response Handlers
|
||||
The response handler is a function that receives the data from a request handler, as well as an instance of Vis object.
|
||||
Its job is to convert the data to a format visualization can use. By default 'default' request handler is used
|
||||
which produces a table representation of the data. The data object will then be passed to visualization.
|
||||
This response matches the visData property of the <visualization> directive.
|
||||
|
||||
[[development-default-response-handler]]
|
||||
==== default response handler
|
||||
Default response handler will convert pure elasticsearch response to tabular format.
|
||||
|
||||
[[development-none-response-handler]]
|
||||
==== none response handler
|
||||
None response handler is an identity function, which will return the same data it receives.
|
||||
|
||||
[[development-custom-response-handler]]
|
||||
==== custom response handler
|
||||
You can define your custom response handler by providing a function with the following definition:
|
||||
'function (vis, response) { ... }'.
|
||||
|
||||
Function should return the transformed data object that visualization can consume.
|
||||
|
||||
["source","js"]
|
||||
-----------
|
||||
import { VisFactoryProvider } from 'ui/vis/vis_factory';
|
||||
|
||||
const myResponseHandler = (vis, response) => {
|
||||
// transform the response (based on vis object?)
|
||||
const resposne = ... transform data ...;
|
||||
return response;
|
||||
};
|
||||
|
||||
const MyNewVisType(Private) => {
|
||||
const VisFactory = Private(VisFactoryProvider);
|
||||
|
||||
return VisFactory.createAngularVisualization({
|
||||
name: 'my_new_vis',
|
||||
title: 'My New Vis',
|
||||
icon: 'my_icon',
|
||||
description: 'Cool new chart',
|
||||
responseHandler: myResponseHandler
|
||||
});
|
||||
}
|
||||
|
||||
VisTypesRegistryProvider.register(MyNewVisType);
|
||||
-----------
|
||||
|
||||
[[development-vis-object]]
|
||||
=== Vis object
|
||||
The `vis` object holds the visualization state and is the window into kibana:
|
||||
|
||||
- *vis.params*: holds the visualization parameters
|
||||
- *vis.indexPattern*: selected index pattern object
|
||||
- *vis.getState()*: gets current visualization state
|
||||
- *vis.updateState()*: updates current state with values from `vis.params`
|
||||
- *vis.resetState()*: resets `vis.params` to the values in the current state
|
||||
- *vis.forceReload()*: forces whole cycle (request handler gets called)
|
||||
- *vis.getUiState()*: gets UI state of visualization
|
||||
- *vis.uiStateVal(name, val)*: updates a property in UI state
|
||||
- *vis.isEditorMode()*: returns true if in editor mode
|
||||
- *vis.API.timeFilter*: allows you to access time picker
|
||||
- *vis.API.events.click*: default click handler
|
||||
- *vis.API.events.brush*: default brush handler
|
||||
|
||||
The visualization gets all its parameters in `vis.params`, which are default values merged with the current state.
|
||||
If the visualization needs to update the current state, it should update the `vis.params` and call `vis.updateState()`
|
||||
which will inform <visualize> about the change, which will call request and response handler and then your
|
||||
visualization's render method.
|
||||
|
||||
For the parameters that should not be saved with the visualization you should use the UI state.
|
||||
These hold viewer-specific state, such as popup open/closed, custom colors applied to the series etc.
|
||||
|
||||
You can access filter bar and time picker through the objects defined on `vis.API`
|
||||
|
||||
[[development-vis-timefilter]]
|
||||
==== timeFilter
|
||||
|
||||
Update the timefilter time values and call update() method on it to update time picker
|
||||
["source","js"]
|
||||
-----------
|
||||
timefilter.time.from = moment(ranges.xaxis.from);
|
||||
timefilter.time.to = moment(ranges.xaxis.to);
|
||||
timefilter.time.mode = 'absolute';
|
||||
timefilter.update();
|
||||
-----------
|
|
@ -0,0 +1,106 @@
|
|||
[[development-embedding-visualizations]]
|
||||
=== Embedding Visualizations
|
||||
|
||||
There are two different angular directives you can use to insert a visualization in your page.
|
||||
To display an already saved visualization, use the `<visualize>` directive.
|
||||
To reuse an existing Visualization implementation for a more custom purpose, use the `<visualization>` directive instead.
|
||||
|
||||
==== `<visualize>` directive
|
||||
The `<visualize>` directive takes care of loading data, parsing data, rendering the editor
|
||||
(if the Visualization is in edit mode) and rendering the visualization.
|
||||
The directive takes a savedVis object for its configuration.
|
||||
It is the easiest way to add visualization to your page under the assumption that
|
||||
the visualization you are trying to display is saved in kibana.
|
||||
If that is not the case, take a look at using `<visualization>` directive.
|
||||
|
||||
The simplest way is to just pass `saved-id` to `<visualize>`:
|
||||
|
||||
`<visualize saved-id="'447d2930-9eb2-11e7-a956-5558df96e706'"></visualize>`
|
||||
|
||||
For the above to work with time based visualizations time picker must be present (enabled) on the page. For scenarios
|
||||
where timepicker is not available time range can be passed in as additional parameter:
|
||||
|
||||
`<visualize saved-id="'447d2930-9eb2-11e7-a956-5558df96e706'"
|
||||
time-range="{ max: '2017-09-21T21:59:59.999Z', min: '2017-09-18T22:00:00.000Z' }"></visualize>`
|
||||
|
||||
Once <visualize> is done rendering the element will emit `renderComplete` event.
|
||||
|
||||
When more control is required over the visualization you may prefer to load the saved object yourself and then pass it
|
||||
to `<visualize>`
|
||||
|
||||
`<visualize saved-obj='savedVis' app-state='appState' ui-state='uiState' editor-mode='false'></visualize>` where
|
||||
|
||||
`savedVis` is an instance of savedVisualization object, which can be retrieved using `savedVisualizations` service
|
||||
which is explained later in this documentation.
|
||||
|
||||
`appState` is an instance of `AppState`. <visualize> is expecting two keys defined on AppState:
|
||||
|
||||
- `filters` which is an instance of searchSource filter object and
|
||||
- `query` which is an instance of searchSource query object
|
||||
|
||||
If `appState` is not provided, `<visualize>` will not monitor the `AppState`.
|
||||
|
||||
`uiState` should be an instance of `PersistedState`. if not provided visualize will generate one,
|
||||
but you will have no access to it. It is used to store viewer specific information like whether the legend is toggled on or off.
|
||||
|
||||
`editor-mode` defines if <visualize> should render in editor or in view mode.
|
||||
|
||||
*code example: Showing a saved visualization, without linking to querybar or filterbar.*
|
||||
["source","html"]
|
||||
-----------
|
||||
<div ng-controller="KbnTestController" class="test_vis">
|
||||
<visualize saved-obj='savedVis'></visualize>
|
||||
</div>
|
||||
-----------
|
||||
["source","js"]
|
||||
-----------
|
||||
import { uiModules } from 'ui/modules';
|
||||
|
||||
uiModules.get('kibana')
|
||||
.controller('KbnTestController', function ($scope, AppState, savedVisualizations) {
|
||||
const visId = 'enter_your_vis_id';
|
||||
savedVisualizations.get(visId).then(savedVis => $scope.savedObj = savedVis);
|
||||
});
|
||||
-----------
|
||||
|
||||
When <visualize> is ready it will emit `ready:vis` event on the root scope.
|
||||
When <visualize> is done rendering it will emit `renderComplete` event on the element.
|
||||
|
||||
==== `<visualization>` directive
|
||||
The `<visualization>` directive takes a visualization configuration and data.
|
||||
|
||||
`<visualization vis='vis' vis-data='visData' ui-state='uiState' ></visualization>` where
|
||||
|
||||
`vis` is an instance of `Vis` object. The constructor takes 3 parameters:
|
||||
|
||||
- `indexPattern` <string>: the indexPattern you want to pass to the visualization
|
||||
- `visConfig` <object>: the configuration object
|
||||
- `uiState` <object>: uiState object you want to pass to Vis. If not provided Vis will create its own.
|
||||
|
||||
`visData` is the data object. Each visualization defines a `responseHandler`, which defines the format of this object.
|
||||
|
||||
`uiState` is an instance of PersistedState. Visualizations use it to keep track of their current state. If not provided
|
||||
`<visualization>` will create its own (but you won't be able to check its values)
|
||||
|
||||
*code example: create single metric visualization*
|
||||
["source","html"]
|
||||
-----------
|
||||
<div ng-controller="KbnTestController" class="test_vis">
|
||||
<visualization vis='vis' vis-data='visData'></visualize>
|
||||
</div>
|
||||
-----------
|
||||
["source","js"]
|
||||
-----------
|
||||
import { uiModules } from 'ui/modules';
|
||||
|
||||
uiModules.get('kibana')
|
||||
.controller('KbnTestController', function ($scope) {
|
||||
const visConfig = {
|
||||
type: 'metric'
|
||||
};
|
||||
$scope.vis = new Vis('.logstash*', visConfig);
|
||||
$scope.visData = [{ columns: [{ title: 'Count' }], rows: [[ 1024 ], [ 256 ]] }];
|
||||
});
|
||||
-----------
|
||||
|
||||
<visualization> will trigger `renderComplete` event on the element once it's done rendering.
|
|
@ -0,0 +1,21 @@
|
|||
[[development-visualize-index]]
|
||||
== Developing Visualizations
|
||||
|
||||
Kibana Visualizations are the easiest way to add additional functionality to Kibana.
|
||||
This part of documentation is split into two parts.
|
||||
The first part tells you all you need to know on how to embed existing Kibana Visualizations in your plugin.
|
||||
The second step explains how to create your own custom visualization.
|
||||
|
||||
[IMPORTANT]
|
||||
==============================================
|
||||
These pages document internal APIs and are not guaranteed to be supported across future versions of Kibana.
|
||||
However, these docs will be kept up-to-date to reflect the current implementation of Visualization plugins in Kibana.
|
||||
==============================================
|
||||
|
||||
* <<development-embedding-visualizations>>
|
||||
* <<development-create-visualization>>
|
||||
|
||||
|
||||
include::development-embedding-visualizations.asciidoc[]
|
||||
|
||||
include::development-create-visualization.asciidoc[]
|
BIN
docs/images/visualize-flow.png
Normal file
BIN
docs/images/visualize-flow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
Loading…
Add table
Add a link
Reference in a new issue