diff --git a/src/core_plugins/metrics/public/directives/vis_editor.js b/src/core_plugins/metrics/public/directives/vis_editor.js
deleted file mode 100644
index 429e2e9721d35..0000000000000
--- a/src/core_plugins/metrics/public/directives/vis_editor.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import React from 'react';
-import { render, unmountComponentAtNode } from 'react-dom';
-import { uiModules } from 'ui/modules';
-import VisEditor from '../components/vis_editor';
-import addScope from '../lib/add_scope';
-import angular from 'angular';
-import createBrushHandler from '../lib/create_brush_handler';
-const app = uiModules.get('apps/metrics/directives');
-app.directive('metricsVisEditor', (timefilter) => {
- return {
- restrict: 'E',
- link: ($scope, $el) => {
- const addToState = ['embedded', 'fields', 'visData'];
- const Component = addScope(VisEditor, $scope, addToState);
- const handleBrush = createBrushHandler($scope, timefilter);
- const handleChange = part => {
- $scope.$evalAsync(() => angular.copy(part, $scope.model));
- };
- render(, $el[0]);
- $scope.$on('$destroy', () => {
- unmountComponentAtNode($el[0]);
- });
- }
- };
-});
-
diff --git a/src/core_plugins/metrics/public/directives/visualization.js b/src/core_plugins/metrics/public/directives/visualization.js
deleted file mode 100644
index a0e7630f710ba..0000000000000
--- a/src/core_plugins/metrics/public/directives/visualization.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import _ from 'lodash';
-import $ from 'jquery';
-import React from 'react';
-import { render, unmountComponentAtNode } from 'react-dom';
-import Visualization from '../components/visualization';
-import addScope from '../lib/add_scope';
-import { uiModules } from 'ui/modules';
-import createBrushHandler from '../lib/create_brush_handler';
-
-const app = uiModules.get('apps/metrics/directives');
-app.directive('metricsVisualization', (timefilter, $timeout) => {
- return {
- restrict: 'E',
- link: ($scope, $el) => {
- const addToState = ['model', 'visData', 'reversed'];
- const Component = addScope(Visualization, $scope, addToState);
- const handleBrush = createBrushHandler($scope, timefilter);
- render(, $el[0]);
- $scope.$on('$destroy', () => unmountComponentAtNode($el[0]));
-
- // For Metrics, Gauges and markdown visualizations we want to hide the
- // panel title because it just doens't make sense to show it.
- // This is wrapped in a timeout so it happens after the directive is mouted.
- // otherwise the .panel might not be available.
- $timeout(() => {
- const panel = $($el[0]).parents('.panel');
- if (panel.length) {
- const panelHeading = panel.find('.panel-heading');
- const panelTitle = panel.find('.panel-title');
- const matchingTypes = ['metric', 'gauge', 'markdown'];
- if (panelHeading.length && panelTitle.length && _.contains(matchingTypes, $scope.model.type)) {
- panel.css({ position: 'relative' });
- panelHeading.css({
- position: 'absolute',
- top: 0,
- right: 0,
- zIndex: 100
- });
- panelTitle.css({ display: 'none' });
- }
- }
- }, 1);
- }
- };
-});
-
diff --git a/src/core_plugins/metrics/public/kbn_vis_types/editor.html b/src/core_plugins/metrics/public/kbn_vis_types/editor.html
deleted file mode 100644
index afdd392f5f9d8..0000000000000
--- a/src/core_plugins/metrics/public/kbn_vis_types/editor.html
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/src/core_plugins/metrics/public/kbn_vis_types/editor_controller.js b/src/core_plugins/metrics/public/kbn_vis_types/editor_controller.js
index 9c775bcc256d3..d8dd9de9d490f 100644
--- a/src/core_plugins/metrics/public/kbn_vis_types/editor_controller.js
+++ b/src/core_plugins/metrics/public/kbn_vis_types/editor_controller.js
@@ -1,104 +1,30 @@
-import { uiModules } from 'ui/modules';
-import '../services/executor';
-import createNewPanel from '../lib/create_new_panel';
-import '../directives/vis_editor';
-import _ from 'lodash';
-import angular from 'angular';
-import { FilterBarQueryFilterProvider } from 'ui/filter_bar/query_filter';
+import React from 'react';
+import { render, unmountComponentAtNode } from 'react-dom';
-const app = uiModules.get('kibana/metrics_vis', ['kibana']);
-app.controller('MetricsEditorController', (
- $location,
- $element,
- $scope,
- Private,
- timefilter,
- metricsExecutor
-) => {
-
- $scope.embedded = $location.search().embed === 'true';
- const queryFilter = Private(FilterBarQueryFilterProvider);
- const createFetch = Private(require('../lib/fetch'));
- const fetch = () => {
- const fn = createFetch($scope);
- return fn().then((resp) => {
- $element.trigger('renderComplete');
- return resp;
- });
- };
- const fetchFields = Private(require('../lib/fetch_fields'));
-
- const debouncedFetch = _.debounce(() => {
- fetch();
- }, 1000, {
- leading: false,
- trailing: true
- });
-
- const debouncedFetchFields = _.debounce(fetchFields($scope), 1000, {
- leading: false,
- trailing: true
- });
-
- // If the model doesn't exist we need to either intialize it with a copy from
- // the $scope.vis._editableVis.params or create a new panel all together.
- if (!$scope.model) {
- if ($scope.vis._editableVis.params.type) {
- $scope.model = _.assign({}, $scope.vis._editableVis.params);
- } else {
- $scope.model = createNewPanel();
- angular.copy($scope.model, $scope.vis._editableVis.params);
- }
+class ReactEditorController {
+ constructor(el, vis) {
+ this.el = el;
+ this.vis = vis;
}
- $scope.$watchCollection('model', newValue => {
- angular.copy(newValue, $scope.vis._editableVis.params);
- $scope.stageEditableVis();
- debouncedFetch();
-
- const patternsToFetch = [];
- // Fetch any missing index patterns
- if (!$scope.fields[newValue.index_pattern]) {
- patternsToFetch.push(newValue.index_pattern);
- }
+ render(visData) {
+ this.visData = visData;
- newValue.series.forEach(series => {
- if (series.override_index_pattern &&
- !$scope.fields[series.series_index_pattern]) {
- patternsToFetch.push(series.series_index_pattern);
- }
+ return new Promise((resolve) => {
+ const Component = this.vis.type.visConfig.component;
+ render(, this.el);
});
+ }
- if (newValue.annotations) {
- newValue.annotations.forEach(item => {
- if (item.index_pattern &&
- !$scope.fields[item.index_pattern]) {
- patternsToFetch.push(item.index_pattern);
- }
- });
- }
-
- if(patternsToFetch.length) {
- debouncedFetchFields(_.unique(patternsToFetch));
+ resize() {
+ if (this.visData) {
+ this.render(this.vis, this.visData);
}
- });
-
- $scope.visData = {};
- $scope.fields = {};
- // All those need to be consolidated
- $scope.$listen(queryFilter, 'fetch', fetch);
- $scope.$on('fetch', fetch);
-
- fetchFields($scope)($scope.model.index_pattern);
-
- // Register fetch
- metricsExecutor.register({ execute: fetch });
-
- // Start the executor
- metricsExecutor.start();
-
- // Destory the executor
- $scope.$on('$destroy', metricsExecutor.destroy);
+ }
-});
+ destroy() {
+ unmountComponentAtNode(this.el);
+ }
+}
+export { ReactEditorController };
diff --git a/src/core_plugins/metrics/public/kbn_vis_types/index.js b/src/core_plugins/metrics/public/kbn_vis_types/index.js
index 19b5d13c8584f..0be7c4592addd 100644
--- a/src/core_plugins/metrics/public/kbn_vis_types/index.js
+++ b/src/core_plugins/metrics/public/kbn_vis_types/index.js
@@ -1,35 +1,37 @@
-import './vis_controller';
-import './editor_controller';
import '../visualizations/less/main.less';
import 'react-select/dist/react-select.css';
import '../less/main.less';
import image from '../images/icon-visualbuilder.svg';
-import { AngularVisTypeProvider } from 'ui/vis/vis_types/angular_vis_type';
-
+import { ReactEditorController } from './editor_controller';
+import { MetricsRequestHandlerProvider } from './request_handler';
+import { VisFactoryProvider } from 'ui/vis/vis_factory';
import { CATEGORY } from 'ui/vis/vis_category';
+
// register the provider with the visTypes registry so that other know it exists
import { VisTypesRegistryProvider } from 'ui/registry/vis_types';
VisTypesRegistryProvider.register(MetricsVisProvider);
export default function MetricsVisProvider(Private) {
- const AngularVisType = Private(AngularVisTypeProvider);
+ const VisFactory = Private(VisFactoryProvider);
+ const metricsRequestHandler = Private(MetricsRequestHandlerProvider).handler;
- // return the visType object, which kibana will use to display and configure new
- // Vis object of this type.
- return new AngularVisType({
+ return VisFactory.createVislibVisualization({
name: 'metrics',
title: 'Visual Builder',
- image,
description: 'Build time-series using a visual pipeline interface',
category: CATEGORY.TIME,
+ image,
isExperimental: true,
- template: require('./vis.html'),
- fullEditor: true,
- params: {
- editor: require('./editor.html')
+ visConfig: {
+ defaults: {
+
+ },
+ component: require('../components/visualization')
+ },
+ editor: ReactEditorController,
+ editorConfig: {
+ component: require('../components/vis_editor')
},
- requiresSearch: false,
- requiresTimePicker: true,
- implementsRenderComplete: true,
+ requestHandler: metricsRequestHandler
});
}
diff --git a/src/core_plugins/metrics/public/kbn_vis_types/request_handler.js b/src/core_plugins/metrics/public/kbn_vis_types/request_handler.js
new file mode 100644
index 0000000000000..9af39926a15a7
--- /dev/null
+++ b/src/core_plugins/metrics/public/kbn_vis_types/request_handler.js
@@ -0,0 +1,43 @@
+import { validateInterval } from '../lib/validate_interval';
+
+const MetricsRequestHandlerProvider = function (Private, Notifier, config, timefilter, $http) {
+ const dashboardContext = Private(require('../../../timelion/public/services/dashboard_context'));
+ const notify = new Notifier({ location: 'Metrics' });
+
+ return {
+ name: 'metrics',
+ handler: function (vis /*, appState, uiState*/) {
+
+ return new Promise((resolve) => {
+ const panel = vis.params;
+ if (panel && panel.id) {
+ const params = {
+ timerange: timefilter.getBounds(),
+ filters: [dashboardContext()],
+ panels: [panel]
+ };
+
+ try {
+ const maxBuckets = config.get('metrics:max_buckets');
+ validateInterval(timefilter, panel, maxBuckets);
+ return $http.post('../api/metrics/vis/data', params)
+ .success(resp => {
+ resolve(resp);
+ })
+ .error(resp => {
+ resolve({});
+ const err = new Error(resp.message);
+ err.stack = resp.stack;
+ notify.error(err);
+ });
+ } catch (e) {
+ notify.error(e);
+ return resolve();
+ }
+ }
+ });
+ }
+ };
+};
+
+export { MetricsRequestHandlerProvider };
diff --git a/src/core_plugins/metrics/public/kbn_vis_types/vis.html b/src/core_plugins/metrics/public/kbn_vis_types/vis.html
deleted file mode 100644
index 59db84f9bc896..0000000000000
--- a/src/core_plugins/metrics/public/kbn_vis_types/vis.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
diff --git a/src/core_plugins/metrics/public/kbn_vis_types/vis_controller.js b/src/core_plugins/metrics/public/kbn_vis_types/vis_controller.js
deleted file mode 100644
index dff2b669c6daf..0000000000000
--- a/src/core_plugins/metrics/public/kbn_vis_types/vis_controller.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import { uiModules } from 'ui/modules';
-import 'ui/state_management/app_state';
-import '../directives/visualization';
-import { FilterBarQueryFilterProvider } from 'ui/filter_bar/query_filter';
-
-const app = uiModules.get('kibana/metrics_vis');
-
-app.controller('MetricsVisController', (
- $scope,
- $element,
- Private,
- timefilter,
- getAppState,
- $location
-) => {
-
- // If we are in the visualize editor context (and not embedded) we should not
- // render the visualizations. This is handled by the editor itself.
- const embedded = $location.search().embed === 'true';
- if (!embedded && $scope.vis._editableVis) {
- return;
- }
- // We need to watch the app state for changes to the dark theme attribute.
- $scope.state = getAppState();
- $scope.$watch('state.options.darkTheme', newValue => {
- $scope.reversed = Boolean(newValue);
- });
-
- const queryFilter = Private(FilterBarQueryFilterProvider);
- const createFetch = Private(require('../lib/fetch'));
- const fetch = () => {
- const fn = createFetch($scope);
- return fn().then((resp) => {
- $element.trigger('renderComplete');
- return resp;
- });
- };
-
-
- $scope.model = $scope.vis.params;
- $scope.$watch('vis.params', fetch);
-
- // All those need to be consolidated
- $scope.$listen(timefilter, 'fetch', fetch);
- $scope.$listen(queryFilter, 'fetch', fetch);
-
- $scope.$on('courier:searchRefresh', fetch);
- $scope.$on('fetch', fetch);
-});
diff --git a/src/core_plugins/metrics/public/lib/add_scope.js b/src/core_plugins/metrics/public/lib/add_scope.js
deleted file mode 100644
index 047501f8fba91..0000000000000
--- a/src/core_plugins/metrics/public/lib/add_scope.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import React from 'react';
-export default function addScope(WrappedComponent, $scope, addToState = []) {
- return React.createClass({
-
- getInitialState() {
- const state = {};
- addToState.forEach(key => {
- state[key] = $scope[key];
- });
- return state;
- },
-
- componentWillMount() {
- this.unsubs = addToState.map(key => {
- return $scope.$watchCollection(key, newValue => {
- const newState = {};
- newState[key] = newValue;
- this.setState(newState);
- });
- });
- },
-
- componentWillUnmount() {
- this.unsubs.forEach(fn => fn());
- },
-
- render() {
- return (
-
- );
- }
- });
-}
diff --git a/src/core_plugins/metrics/public/lib/fetch.js b/src/core_plugins/metrics/public/lib/fetch.js
deleted file mode 100644
index ed4b54a114cde..0000000000000
--- a/src/core_plugins/metrics/public/lib/fetch.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import { validateInterval } from './validate_interval';
-export default (
- timefilter,
- Private,
- Notifier,
- $http,
- config
-) => {
- const dashboardContext = Private(require('../../../timelion/public/services/dashboard_context'));
- const notify = new Notifier({ location: 'Metrics' });
- return $scope => () => {
- const panel = $scope.model;
- if (panel && panel.id) {
- const params = {
- timerange: timefilter.getBounds(),
- filters: [dashboardContext()],
- panels: [panel]
- };
-
- try {
- const maxBuckets = config.get('metrics:max_buckets');
- validateInterval(timefilter, panel, maxBuckets);
- return $http.post('../api/metrics/vis/data', params)
- .success(resp => {
- $scope.visData = resp;
- })
- .error(resp => {
- $scope.visData = {};
- const err = new Error(resp.message);
- err.stack = resp.stack;
- notify.error(err);
- });
- } catch (e) {
- notify.error(e);
- return Promise.resolve();
- }
- }
- return Promise.resolve();
- };
-};
-