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