adding documentation for visualization development (#14252)

* adding documentation for  visualization development
This commit is contained in:
Peter Pisljar 2017-10-03 13:07:28 +02:00 committed by GitHub
parent 1ea82fa869
commit 4816079ed7
5 changed files with 543 additions and 0 deletions

View file

@ -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[]

View file

@ -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();
-----------

View file

@ -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.

View file

@ -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[]

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB