diff --git a/src/core_plugins/kbn_vislib_vis_types/public/controls/gauge_options.html b/src/core_plugins/kbn_vislib_vis_types/public/controls/gauge_options.html index 4c8afa31f64e98..fee2c53a65b5c0 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/controls/gauge_options.html +++ b/src/core_plugins/kbn_vislib_vis_types/public/controls/gauge_options.html @@ -8,7 +8,7 @@ id="gaugeType" class="kuiSelect kuiSideBarSelect" ng-model="vis.params.gauge.gaugeType" - ng-options="mode for mode in vis.type.params.gaugeTypes" + ng-options="mode for mode in collections.gaugeTypes" > @@ -183,7 +183,7 @@ id="colorSchema" class="kuiSelect kuiSideBarSelect" ng-model="vis.params.gauge.colorSchema" - ng-options="mode for mode in vis.type.params.colorSchemas" + ng-options="mode for mode in collections.colorSchemas" >
reset colors
@@ -255,7 +255,7 @@ id="gaugeColorMode" class="kuiSelect kuiSideBarSelect" ng-model="vis.params.gauge.gaugeColorMode" - ng-options="mode for mode in vis.type.params.gaugeColorMode" + ng-options="mode for mode in collections.gaugeColorMode" > diff --git a/src/core_plugins/kbn_vislib_vis_types/public/controls/gauge_options.js b/src/core_plugins/kbn_vislib_vis_types/public/controls/gauge_options.js index 6523c57d644338..e2849ac43797b6 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/controls/gauge_options.js +++ b/src/core_plugins/kbn_vislib_vis_types/public/controls/gauge_options.js @@ -9,7 +9,7 @@ module.directive('gaugeOptions', function () { template: gaugeOptionsTemplate, replace: true, link: function ($scope) { - + $scope.collections = $scope.vis.type.editorConfig.collections; $scope.showColorRange = true; $scope.$watch('vis.params.gauge.gaugeType', type => { diff --git a/src/core_plugins/kbn_vislib_vis_types/public/controls/heatmap_options.html b/src/core_plugins/kbn_vislib_vis_types/public/controls/heatmap_options.html index efc6857772e675..ed2affb0e34c0d 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/controls/heatmap_options.html +++ b/src/core_plugins/kbn_vislib_vis_types/public/controls/heatmap_options.html @@ -8,7 +8,7 @@ id="colorSchema" class="kuiSelect kuiSideBarSelect" ng-model="vis.params.colorSchema" - ng-options="mode for mode in vis.editorConfig.collections.colorSchemas" + ng-options="mode for mode in collections.colorSchemas" >
@@ -141,7 +141,7 @@ ng-click="removeRange($index)" class="kuiButton kuiButton--danger kuiButton--small" > - + @@ -149,7 +149,7 @@

- + Required: You must specify at least one range.

diff --git a/src/core_plugins/kbn_vislib_vis_types/public/controls/heatmap_options.js b/src/core_plugins/kbn_vislib_vis_types/public/controls/heatmap_options.js index bfdaad8071068c..2810cc72ce9064 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/controls/heatmap_options.js +++ b/src/core_plugins/kbn_vislib_vis_types/public/controls/heatmap_options.js @@ -9,6 +9,9 @@ module.directive('heatmapOptions', function () { template: heatmapOptionsTemplate, replace: true, link: function ($scope) { + + $scope.collections = $scope.vis.type.editorConfig.collections; + const verticalRotation = 270; $scope.showColorRange = false; $scope.showLabels = false; diff --git a/src/core_plugins/kbn_vislib_vis_types/public/editors/__tests__/point_series.js b/src/core_plugins/kbn_vislib_vis_types/public/editors/__tests__/point_series.js index 8b46f02d555b49..b0bf4191a6b202 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/editors/__tests__/point_series.js +++ b/src/core_plugins/kbn_vislib_vis_types/public/editors/__tests__/point_series.js @@ -20,7 +20,7 @@ describe('point series editor', function () { function makeConfig() { return { type: 'line', - params: lineVisType.params.defaults, + params: lineVisType.visConfig.defaults, aggs: [ { type: 'count', schema: 'metric', params: { field: 'bytes' } }, { type: 'terms', schema: 'segment', params: { field: 'machine.os' } }, diff --git a/src/core_plugins/kbn_vislib_vis_types/public/editors/heatmap.html b/src/core_plugins/kbn_vislib_vis_types/public/editors/heatmap.html index fa86afbd9d3c7f..1d1d5e418a5911 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/editors/heatmap.html +++ b/src/core_plugins/kbn_vislib_vis_types/public/editors/heatmap.html @@ -32,7 +32,7 @@ id="legendPosition" class="kuiSelect kuiSideBarSelect" ng-model="vis.params.legendPosition" - ng-options="position.value as position.text for position in vis.type.params.legendPositions" + ng-options="position.value as position.text for position in vis.type.editorConfig.collections.legendPositions" > diff --git a/src/core_plugins/kbn_vislib_vis_types/public/gauge.js b/src/core_plugins/kbn_vislib_vis_types/public/gauge.js index 72441d4b25fff3..a35390116902ca 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/gauge.js +++ b/src/core_plugins/kbn_vislib_vis_types/public/gauge.js @@ -18,6 +18,7 @@ export default function GaugeVisType(Private) { category: CATEGORY.DATA, visConfig: { defaults: { + type:'gauge', addTooltip: true, addLegend: true, diff --git a/src/core_plugins/kbn_vislib_vis_types/public/heatmap.js b/src/core_plugins/kbn_vislib_vis_types/public/heatmap.js index 2b68b597c07fbd..656c94c71697df 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/heatmap.js +++ b/src/core_plugins/kbn_vislib_vis_types/public/heatmap.js @@ -46,7 +46,7 @@ export default function HeatmapVisType(Private) { }, }, editorConfig: { - collectons: { + collections: { legendPositions: [{ value: 'left', text: 'left', diff --git a/src/core_plugins/kibana/public/dashboard/panel/panel.html b/src/core_plugins/kibana/public/dashboard/panel/panel.html index 6db60243465269..2f94871efead38 100644 --- a/src/core_plugins/kibana/public/dashboard/panel/panel.html +++ b/src/core_plugins/kibana/public/dashboard/panel/panel.html @@ -83,8 +83,10 @@ saved-obj="savedObj" app-state="appState" ui-state="uiState" + data-shared-item data-title="{{savedObj.title}}" data-description="{{savedObj.description}}" + render-counter class="panel-content"> diff --git a/src/core_plugins/kibana/public/visualize/editor/editor.html b/src/core_plugins/kibana/public/visualize/editor/editor.html index 909b8d0991ba98..e50ff1eee2265e 100644 --- a/src/core_plugins/kibana/public/visualize/editor/editor.html +++ b/src/core_plugins/kibana/public/visualize/editor/editor.html @@ -90,6 +90,8 @@ ui-state="uiState" app-state="state" editor-mode="true" + data-shared-item + render-counter data-title="{{savedVis.lastSavedTitle}}" data-description="{{savedVis.description}}" show-spy-panel="chrome.getVisible()"> diff --git a/src/core_plugins/kibana/public/visualize/editor/editor.js b/src/core_plugins/kibana/public/visualize/editor/editor.js index 665034db1177f7..8d7fc1df4cfe0c 100644 --- a/src/core_plugins/kibana/public/visualize/editor/editor.js +++ b/src/core_plugins/kibana/public/visualize/editor/editor.js @@ -1,9 +1,6 @@ import _ from 'lodash'; import 'plugins/kibana/visualize/saved_visualizations/saved_visualizations'; - -// import 'plugins/kibana/visualize/editor/sidebar'; import 'ui/vis/editors/default/sidebar'; - import 'plugins/kibana/visualize/editor/agg_filter'; import 'ui/visualize'; import 'ui/collapsible_sidebar'; diff --git a/src/core_plugins/kibana/public/visualize/index.js b/src/core_plugins/kibana/public/visualize/index.js index 76bf680a314c0a..c0bf9caab21458 100644 --- a/src/core_plugins/kibana/public/visualize/index.js +++ b/src/core_plugins/kibana/public/visualize/index.js @@ -23,9 +23,9 @@ import { BasicResponseHandlerProvider } from 'ui/vis/response_handlers/basic'; import { defaultEditor } from 'ui/vis/editors/default/default'; -import { RequestHandlersRegistryProvider } from 'ui/registry/request_handlers'; -import { ResponseHandlersRegistryProvider } from 'ui/registry/response_handlers'; -import { EditorTypesRegistryProvider } from 'ui/registry/editor_types'; +import { VisRequestHandlersRegistryProvider } from 'ui/registry/vis_request_handlers'; +import { VisResponseHandlersRegistryProvider } from 'ui/registry/vis_response_handlers'; +import { VisEditorTypesRegistryProvider } from 'ui/registry/vis_editor_types'; uiRoutes .defaults(/visualize/, { @@ -39,9 +39,9 @@ uiRoutes // preloading SavedObjectRegistryProvider.register(savedVisualizationProvider); -RequestHandlersRegistryProvider.register(CourierRequestHandlerProvider); -RequestHandlersRegistryProvider.register(noneRequestHandlerProvider); -ResponseHandlersRegistryProvider.register(noneResponseHandler); -ResponseHandlersRegistryProvider.register(BasicResponseHandlerProvider); -EditorTypesRegistryProvider.register(defaultEditor); +VisRequestHandlersRegistryProvider.register(CourierRequestHandlerProvider); +VisRequestHandlersRegistryProvider.register(noneRequestHandlerProvider); +VisResponseHandlersRegistryProvider.register(noneResponseHandler); +VisResponseHandlersRegistryProvider.register(BasicResponseHandlerProvider); +VisEditorTypesRegistryProvider.register(defaultEditor); diff --git a/src/core_plugins/markdown_vis/public/__tests__/markdown_vis_controller.js b/src/core_plugins/markdown_vis/public/__tests__/markdown_vis_controller.js index f9dc317de5f2bb..5898bf6899f20f 100644 --- a/src/core_plugins/markdown_vis/public/__tests__/markdown_vis_controller.js +++ b/src/core_plugins/markdown_vis/public/__tests__/markdown_vis_controller.js @@ -8,6 +8,7 @@ describe('markdown vis controller', function () { beforeEach(ngMock.module('kibana/markdown_vis')); beforeEach(ngMock.inject(function ($rootScope, $controller) { $scope = $rootScope.$new(); + $scope.renderComplete = () => {}; const $element = $('
'); $controller('KbnMarkdownVisController', { $scope, $element }); $scope.$digest(); diff --git a/src/core_plugins/metrics/public/components/vis_editor.js b/src/core_plugins/metrics/public/components/vis_editor.js index a6144b468f125f..b677a707cf5f8e 100644 --- a/src/core_plugins/metrics/public/components/vis_editor.js +++ b/src/core_plugins/metrics/public/components/vis_editor.js @@ -9,20 +9,29 @@ class VisEditor extends Component { constructor(props) { super(props); - this.state = { model: props.vis.params }; + this.state = { model: props.vis.params, dirty: false, autoApply: true }; this.onBrush = brushHandler(props.vis.API.timeFilter); } render() { const handleChange = (part) => { const nextModel = { ...this.state.model, ...part }; - this.setState({ model: nextModel }); - if (this.props.onChange || true) { - console.log(nextModel); - this.props.vis.params = nextModel; + + this.props.vis.params = nextModel; + if (this.state.autoApply) { this.props.vis.updateState(); - //this.props.onChange(nextModel); } + + this.setState({ model: nextModel, dirty: !this.state.autoApply }); + }; + + const handleAutoApplyToggle = (part) => { + this.setState({ autoApply: part.target.checked }); + }; + + const handleCommit = () => { + this.props.vis.updateState(); + this.setState({ dirty: false }); }; if (!this.props.vis.isEditorMode()) { @@ -44,13 +53,13 @@ class VisEditor extends Component { model={model} onChange={handleChange} /> ); } + return null; } + componentDidMount() { + this.props.renderComplete(); + } + } VisEditor.propTypes = { diff --git a/src/core_plugins/metrics/public/lib/__tests__/add_scope.js b/src/core_plugins/metrics/public/lib/__tests__/add_scope.js deleted file mode 100644 index c2728ce598c0a7..00000000000000 --- a/src/core_plugins/metrics/public/lib/__tests__/add_scope.js +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react'; -import { expect } from 'chai'; -import { shallow } from 'enzyme'; -import sinon from 'sinon'; -import addScope from '../add_scope'; - -const Component = React.createClass({ - render() { - return (
); - } -}); - -describe('addScope()', () => { - - let unsubStub; - let watchCollectionStub; - let $scope; - - beforeEach(() => { - unsubStub = sinon.stub(); - watchCollectionStub = sinon.stub().returns(unsubStub); - $scope = { - $watchCollection: watchCollectionStub, - testOne: 1, - testTwo: 2 - }; - }); - - it('adds $scope variables as props to wrapped component', () => { - const WrappedComponent = addScope(Component, $scope, ['testOne', 'testTwo']); - const wrapper = shallow(); - expect(wrapper.state('testOne')).to.equal(1); - expect(wrapper.state('testTwo')).to.equal(2); - }); - - it('calls $scope.$watchCollection on each scoped item', () => { - const WrappedComponent = addScope(Component, $scope, ['testOne', 'testTwo']); - shallow(); - expect(watchCollectionStub.calledTwice).to.equal(true); - expect(watchCollectionStub.firstCall.args[0]).to.equal('testOne'); - expect(watchCollectionStub.secondCall.args[0]).to.equal('testTwo'); - }); - - it('unsubscribes from watches', () => { - const WrappedComponent = addScope(Component, $scope, ['testOne', 'testTwo']); - const wrapper = shallow(); - wrapper.unmount(); - expect(unsubStub.calledTwice).to.equal(true); - }); - - it('updates state when watch is called', () => { - const WrappedComponent = addScope(Component, $scope, ['testOne']); - const wrapper = shallow(); - watchCollectionStub.firstCall.args[1].call(null, 3); - expect(wrapper.state('testOne')).to.equal(3); - }); - -}); diff --git a/src/core_plugins/metrics/public/lib/__tests__/create_brush_handler.js b/src/core_plugins/metrics/public/lib/__tests__/create_brush_handler.js index d57f05e4bbf7fe..9c1b865215ccbd 100644 --- a/src/core_plugins/metrics/public/lib/__tests__/create_brush_handler.js +++ b/src/core_plugins/metrics/public/lib/__tests__/create_brush_handler.js @@ -1,29 +1,19 @@ import createBrushHandler from '../create_brush_handler'; -import sinon from 'sinon'; import moment from 'moment'; import { expect } from 'chai'; describe('createBrushHandler', () => { - - let evalAsyncStub; - let $scope; let timefilter; let fn; let range; beforeEach(() => { - evalAsyncStub = sinon.stub().yields(); - $scope = { $evalAsync: evalAsyncStub }; - timefilter = { time: {} }; - fn = createBrushHandler($scope, timefilter); + timefilter = { time: {}, update: () => {} }; + fn = createBrushHandler(timefilter); range = { xaxis: { from: '2017-01-01T00:00:00Z', to: '2017-01-01T00:10:00Z' } }; fn(range); }); - it('returns brushHandler() that calls $scope.$evalAsync()', () => { - expect(evalAsyncStub.calledOnce).to.equal(true); - }); - it('returns brushHandler() that updates timefilter', () => { expect(timefilter.time.from).to.equal(moment(range.xaxis.from).toISOString()); expect(timefilter.time.to).to.equal(moment(range.xaxis.to).toISOString()); diff --git a/src/core_plugins/region_map/public/region_map_controller.js b/src/core_plugins/region_map/public/region_map_controller.js index 9507529f4e8256..905dc3c9b69107 100644 --- a/src/core_plugins/region_map/public/region_map_controller.js +++ b/src/core_plugins/region_map/public/region_map_controller.js @@ -6,8 +6,7 @@ import { KibanaMap } from '../../tile_map/public/kibana_map'; import ChoroplethLayer from './choropleth_layer'; import { truncatedColorMaps } from 'ui/vislib/components/color/truncated_colormaps'; import AggResponsePointSeriesTooltipFormatterProvider from './tooltip_formatter'; -import { ResizeCheckerProvider } from 'ui/resize_checker'; -import 'ui/vis_maps/lib/service_settings'; +import '../../tile_map/public/lib/service_settings'; const module = uiModules.get('kibana/region_map', ['kibana']); @@ -15,18 +14,18 @@ module.controller('KbnRegionMapController', function ($scope, $element, Private, serviceSettings, config) { const tooltipFormatter = Private(AggResponsePointSeriesTooltipFormatterProvider); - const ResizeChecker = Private(ResizeCheckerProvider); const notify = new Notifier({ location: 'Region map' }); - const resizeChecker = new ResizeChecker($element); + let kibanaMap = null; - resizeChecker.on('resize', () => { + let choroplethLayer = null; + const kibanaMapReady = makeKibanaMap(); + + $scope.$watch('resize', () => { if (kibanaMap) { kibanaMap.resize(); } }); - let choroplethLayer = null; - const kibanaMapReady = makeKibanaMap(); $scope.$watch('esResponse', async function (response) { kibanaMapReady.then(() => { diff --git a/src/core_plugins/region_map/public/region_map_vis_params.html b/src/core_plugins/region_map/public/region_map_vis_params.html index 45a218267a3b69..9aba92115e880d 100644 --- a/src/core_plugins/region_map/public/region_map_vis_params.html +++ b/src/core_plugins/region_map/public/region_map_vis_params.html @@ -15,9 +15,9 @@ id="regionMap" class="kuiSelect kuiSideBarSelect" ng-model="vis.params.selectedLayer" - ng-options="layer.name for layer in vis.type.params.vectorLayers" + ng-options="layer.name for layer in collections.vectorLayers" ng-change="onLayerChange()" - ng-init="vis.params.selectedLayer=vis.type.params.vectorLayers[0]" + ng-init="vis.params.selectedLayer=collections.vectorLayers[0]" >
@@ -51,7 +51,7 @@ id="colorSchema" class="kuiSelect kuiSideBarSelect" ng-model="vis.params.colorSchema" - ng-options="mode for mode in vis.type.params.colorSchemas" + ng-options="mode for mode in collections.colorSchemas" > diff --git a/src/core_plugins/region_map/public/region_map_vis_params.js b/src/core_plugins/region_map/public/region_map_vis_params.js index 88189c43307e56..5b286f2d8c06c9 100644 --- a/src/core_plugins/region_map/public/region_map_vis_params.js +++ b/src/core_plugins/region_map/public/region_map_vis_params.js @@ -13,11 +13,13 @@ uiModules.get('kibana/region_map') template: regionMapVisParamsTemplate, link: function ($scope) { + $scope.collections = $scope.vis.type.editorConfig.collections; + $scope.onLayerChange = onLayerChange; serviceSettings.getFileLayers() .then(function (layersFromService) { - const newVectorLayers = $scope.vis.type.params.vectorLayers.slice(); + const newVectorLayers = $scope.collections.vectorLayers.slice(); for (let i = 0; i < layersFromService.length; i += 1) { const layerFromService = layersFromService[i]; const alreadyAdded = newVectorLayers.some((layer) =>_.eq(layerFromService, layer)); @@ -26,10 +28,10 @@ uiModules.get('kibana/region_map') } } - $scope.vis.type.params.vectorLayers = newVectorLayers; + $scope.collections.vectorLayers = newVectorLayers; - if ($scope.vis.type.params.vectorLayers[0] && !$scope.vis.params.selectedLayer) { - $scope.vis.params.selectedLayer = $scope.vis.type.params.vectorLayers[0]; + if ($scope.collections.vectorLayers[0] && !$scope.vis.params.selectedLayer) { + $scope.vis.params.selectedLayer = $scope.collections.vectorLayers[0]; onLayerChange(); } diff --git a/src/core_plugins/table_vis/public/__tests__/_table_vis.js b/src/core_plugins/table_vis/public/__tests__/_table_vis.js index 5b1b44776b607d..6b4414d4cefccb 100644 --- a/src/core_plugins/table_vis/public/__tests__/_table_vis.js +++ b/src/core_plugins/table_vis/public/__tests__/_table_vis.js @@ -29,9 +29,10 @@ describe('Integration', function () { $rootScope.vis = vis; $rootScope.esResponse = esResponse; $rootScope.uiState = require('fixtures/mock_ui_state'); - $el = $(''); + $el = $(''); $compile($el)($rootScope); $rootScope.$apply(); + } function OneRangeVis(params) { @@ -89,20 +90,25 @@ describe('Integration', function () { it('passes the table groups to the kbnAggTableGroup directive', function () { init(new OneRangeVis(), fixtures.oneRangeBucket); - const $atg = $el.find('kbn-agg-table-group').first(); - expect($atg.size()).to.be(1); - expect($atg.attr('group')).to.be('tableGroups'); - expect($atg.isolateScope().group).to.be($atg.scope().tableGroups); + $rootScope.$on('renderComplete', () => { + const $atg = $el.find('kbn-agg-table-group').first(); + expect($atg.size()).to.be(1); + expect($atg.attr('group')).to.be('tableGroups'); + expect($atg.isolateScope().group).to.be($atg.scope().tableGroups); + }); + }); it('displays an error if the search had no hits', function () { init(new OneRangeVis(), { hits: { total: 0, hits: [] } }); - expect($el.find('kbn-agg-table-group').size()).to.be(0); + $rootScope.$on('renderComplete', () => { + expect($el.find('kbn-agg-table-group').size()).to.be(0); - const $err = $el.find('.table-vis-error'); - expect($err.size()).to.be(1); - expect($err.text().trim()).to.be('No results found'); + const $err = $el.find('.table-vis-error'); + expect($err.size()).to.be(1); + expect($err.text().trim()).to.be('No results found'); + }); }); it('displays an error if the search hits, but didn\'t create any rows', function () { @@ -121,10 +127,12 @@ describe('Integration', function () { init(new ThreeTermVis(visParams), resp); - expect($el.find('kbn-agg-table-group').size()).to.be(0); + $rootScope.$on('renderComplete', () => { + expect($el.find('kbn-agg-table-group').size()).to.be(0); - const $err = $el.find('.table-vis-error'); - expect($err.size()).to.be(1); - expect($err.text().trim()).to.be('No results found'); + const $err = $el.find('.table-vis-error'); + expect($err.size()).to.be(1); + expect($err.text().trim()).to.be('No results found'); + }); }); }); diff --git a/src/core_plugins/table_vis/public/__tests__/_table_vis_controller.js b/src/core_plugins/table_vis/public/__tests__/_table_vis_controller.js index bf5dd7c5870978..6f458fa3aa4132 100644 --- a/src/core_plugins/table_vis/public/__tests__/_table_vis_controller.js +++ b/src/core_plugins/table_vis/public/__tests__/_table_vis_controller.js @@ -58,6 +58,7 @@ describe('Controller', function () { $rootScope.vis = vis; $rootScope.uiState = new AppState({ uiState: {} }).makeStateful('uiState'); + $rootScope.renderComplete = () => {}; $rootScope.newScope = function (scope) { $scope = scope; }; $el = $('
') diff --git a/src/core_plugins/tagcloud/public/tag_cloud_controller.js b/src/core_plugins/tagcloud/public/tag_cloud_controller.js index 0680183696e245..3beea10eb69f51 100644 --- a/src/core_plugins/tagcloud/public/tag_cloud_controller.js +++ b/src/core_plugins/tagcloud/public/tag_cloud_controller.js @@ -20,6 +20,7 @@ module.controller('KbnTagCloudController', function ($scope, $element, Private, const aggConfigResult = new AggConfigResult(aggs[0], false, event, event); clickHandler({ point: { aggConfigResult: aggConfigResult } }); }); + tagCloud.on('renderComplete', () => { const truncatedMessage = containerNode.querySelector('.tagcloud-truncated-message'); @@ -33,13 +34,9 @@ module.controller('KbnTagCloudController', function ($scope, $element, Private, const bucketName = containerNode.querySelector('.tagcloud-custom-label'); bucketName.innerHTML = `${$scope.vis.aggs[0].makeLabel()} - ${$scope.vis.aggs[1].makeLabel()}`; - - truncatedMessage.style.display = truncated ? 'block' : 'none'; - const status = tagCloud.getStatus(); - if (TagCloud.STATUS.COMPLETE === status) { incompleteMessage.style.display = 'none'; } else if (TagCloud.STATUS.INCOMPLETE === status) { @@ -87,14 +84,9 @@ module.controller('KbnTagCloudController', function ($scope, $element, Private, $scope.$watch('vis.params', (options) => tagCloud.setOptions(options)); - $scope.$watch(getContainerSize, _.debounce(() => { + $scope.$watch('resize', () => { tagCloud.resize(); - }, 1000, { trailing: true }), true); - - - function getContainerSize() { - return { width: $element.width(), height: $element.height() }; - } + }); function getValue(metricsAgg, bucket) { let size = metricsAgg.getValue(bucket); diff --git a/src/core_plugins/tile_map/public/__tests__/geohash_layer.js b/src/core_plugins/tile_map/public/__tests__/geohash_layer.js index c61ed5d991bf8e..147f5fee0769dd 100644 --- a/src/core_plugins/tile_map/public/__tests__/geohash_layer.js +++ b/src/core_plugins/tile_map/public/__tests__/geohash_layer.js @@ -1,6 +1,6 @@ import expect from 'expect.js'; -import { KibanaMap } from 'ui/vis_maps/kibana_map'; -import { GeohashLayer } from 'ui/vis_maps/geohash_layer'; +import { KibanaMap } from '../kibana_map'; +import { GeohashLayer } from '../geohash_layer'; import { GeoHashSampleData } from './geohash_sample_data'; describe('kibana_map tests', function () { diff --git a/src/core_plugins/tile_map/public/__tests__/kibana_map.js b/src/core_plugins/tile_map/public/__tests__/kibana_map.js index a3752e42c2d644..a1f6195528755b 100644 --- a/src/core_plugins/tile_map/public/__tests__/kibana_map.js +++ b/src/core_plugins/tile_map/public/__tests__/kibana_map.js @@ -1,5 +1,5 @@ import expect from 'expect.js'; -import { KibanaMap } from 'ui/vis_maps/kibana_map'; +import { KibanaMap } from '../kibana_map'; describe('kibana_map tests', function () { @@ -40,13 +40,13 @@ describe('kibana_map tests', function () { teardownDOM(); }); - it('should instantiate with world in view', function () { + it('should instantiate at zoom level 2', function () { const bounds = kibanaMap.getBounds(); - expect(bounds.bottom_right.lon).to.equal(180); - expect(bounds.top_left.lon).to.equal(-180); + expect(bounds.bottom_right.lon).to.equal(90); + expect(bounds.top_left.lon).to.equal(-90); expect(kibanaMap.getCenter().lon).to.equal(0); expect(kibanaMap.getCenter().lat).to.equal(0); - expect(kibanaMap.getZoomLevel()).to.equal(1); + expect(kibanaMap.getZoomLevel()).to.equal(2); }); it('should resize to fit container', function () { diff --git a/src/ui/public/vis_maps/__tests__/service_settings.js b/src/core_plugins/tile_map/public/__tests__/service_settings.js similarity index 95% rename from src/ui/public/vis_maps/__tests__/service_settings.js rename to src/core_plugins/tile_map/public/__tests__/service_settings.js index a54fb6b1c5ec81..bca96a6a36b588 100644 --- a/src/ui/public/vis_maps/__tests__/service_settings.js +++ b/src/core_plugins/tile_map/public/__tests__/service_settings.js @@ -15,19 +15,18 @@ describe('service_settings (FKA tilemaptest)', function () { const manifestUrl2 = 'https://foobar/v1/manifest'; const manifest = { - 'services': [ - { - 'id': 'tiles_v2', - 'name': 'Elastic Tile Service', - 'manifest': tmsManifestUrl, - 'type': 'tms' - }, - { - 'id': 'geo_layers', - 'name': 'Elastic Layer Service', - 'manifest': vectorManifestUrl, - 'type': 'file' - } + 'services': [{ + 'id': 'tiles_v2', + 'name': 'Elastic Tile Service', + 'manifest': tmsManifestUrl, + 'type': 'tms' + }, + { + 'id': 'geo_layers', + 'name': 'Elastic Layer Service', + 'manifest': vectorManifestUrl, + 'type': 'file' + } ] }; diff --git a/src/core_plugins/tile_map/public/__tests__/tilemap_settings.js b/src/core_plugins/tile_map/public/__tests__/tilemap_settings.js deleted file mode 100644 index e5d8982aac2e50..00000000000000 --- a/src/core_plugins/tile_map/public/__tests__/tilemap_settings.js +++ /dev/null @@ -1,61 +0,0 @@ -import expect from 'expect.js'; -import ngMock from 'ng_mock'; -import url from 'url'; - -describe('tilemaptest - TileMapSettingsTests-deprecated', function () { - let tilemapSettings; - let loadSettings; - - beforeEach(ngMock.module('kibana', ($provide) => { - $provide.decorator('tilemapsConfig', () => ({ - manifestServiceUrl: 'https://proxy-tiles.elastic.co/v1/manifest', - deprecated: { - isOverridden: true, - config: { - url: 'https://tiles.elastic.co/v1/default/{z}/{x}/{y}.png?my_app_name=kibana_tests', - options: { - minZoom: 1, - maxZoom: 10, - attribution: '© [Elastic Tile Service](https://www.elastic.co/elastic_tile_service)' - } - }, - } - })); - })); - - beforeEach(ngMock.inject(function ($injector, $rootScope) { - tilemapSettings = $injector.get('tilemapSettings'); - - loadSettings = () => { - tilemapSettings.loadSettings(); - $rootScope.$digest(); - }; - })); - - describe('getting settings', function () { - beforeEach(function () { - loadSettings(); - }); - - it('should get url', function () { - - const mapUrl = tilemapSettings.getUrl(); - expect(mapUrl).to.contain('{x}'); - expect(mapUrl).to.contain('{y}'); - expect(mapUrl).to.contain('{z}'); - - const urlObject = url.parse(mapUrl, true); - expect(urlObject.hostname).to.be('tiles.elastic.co'); - expect(urlObject.query).to.have.property('my_app_name', 'kibana_tests'); - - }); - - it('should get options', function () { - const options = tilemapSettings.getTMSOptions(); - expect(options).to.have.property('minZoom'); - expect(options).to.have.property('maxZoom'); - expect(options).to.have.property('attribution'); - }); - - }); -}); diff --git a/src/core_plugins/tile_map/public/__tests__/tilemap_settings_mocked.js b/src/core_plugins/tile_map/public/__tests__/tilemap_settings_mocked.js deleted file mode 100644 index 3f34c40e9c1edf..00000000000000 --- a/src/core_plugins/tile_map/public/__tests__/tilemap_settings_mocked.js +++ /dev/null @@ -1,140 +0,0 @@ -import expect from 'expect.js'; -import ngMock from 'ng_mock'; -import url from 'url'; - -describe('tilemaptest - TileMapSettingsTests-mocked', function () { - let tilemapSettings; - let tilemapsConfig; - let loadSettings; - - beforeEach(ngMock.module('kibana', ($provide) => { - $provide.decorator('tilemapsConfig', () => ({ - manifestServiceUrl: 'http://foo.bar/manifest', - deprecated: { - isOverridden: false, - config: { - url: '', - options: { - minZoom: 1, - maxZoom: 10, - attribution: '© [Elastic Tile Service](https://www.elastic.co/elastic_tile_service)' - } - }, - } - })); - })); - - beforeEach(ngMock.inject(($injector, $httpBackend) => { - tilemapSettings = $injector.get('tilemapSettings'); - tilemapsConfig = $injector.get('tilemapsConfig'); - - loadSettings = (expectedUrl) => { - // body and headers copied from https://proxy-tiles.elastic.co/v1/manifest - const MANIFEST_BODY = `{ - "services":[ - { - "id":"road_map", - "url":"https://proxy-tiles.elastic.co/v1/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana", - "minZoom":0, - "maxZoom":12, - "attribution":"© [Elastic Tile Service](https://www.elastic.co/elastic-tile-service)" - } - ] - }`; - - const MANIFEST_HEADERS = { - 'access-control-allow-methods': 'GET, OPTIONS', - 'access-control-allow-origin': '*', - 'content-length': `${MANIFEST_BODY.length}`, - 'content-type': 'application/json; charset=utf-8', - date: (new Date()).toUTCString(), - server: 'tileprox/20170102101655-a02e54d', - status: '200', - }; - - $httpBackend - .expect('GET', expectedUrl ? expectedUrl : () => true) - .respond(MANIFEST_BODY, MANIFEST_HEADERS); - - tilemapSettings.loadSettings(); - - $httpBackend.flush(); - }; - })); - - afterEach(ngMock.inject($httpBackend => { - $httpBackend.verifyNoOutstandingRequest(); - $httpBackend.verifyNoOutstandingExpectation(); - })); - - describe('getting settings', function () { - beforeEach(() => { - loadSettings(); - }); - - - it('should get url', async function () { - - const mapUrl = tilemapSettings.getUrl(); - expect(mapUrl).to.contain('{x}'); - expect(mapUrl).to.contain('{y}'); - expect(mapUrl).to.contain('{z}'); - - const urlObject = url.parse(mapUrl, true); - expect(urlObject).to.have.property('hostname', 'proxy-tiles.elastic.co'); - expect(urlObject.query).to.have.property('my_app_name', 'kibana'); - expect(urlObject.query).to.have.property('elastic_tile_service_tos', 'agree'); - expect(urlObject.query).to.have.property('my_app_version'); - - }); - - it('should get options', async function () { - const options = tilemapSettings.getTMSOptions(); - expect(options).to.have.property('minZoom', 0); - expect(options).to.have.property('maxZoom', 12); - expect(options).to.have.property('attribution').contain('©'); // html entity for ©, ensures that attribution is escaped - }); - - }); - - describe('modify', function () { - function assertQuery(expected) { - const mapUrl = tilemapSettings.getUrl(); - const urlObject = url.parse(mapUrl, true); - Object.keys(expected).forEach(key => { - expect(urlObject.query).to.have.property(key, expected[key]); - }); - } - - it('accepts an object', () => { - tilemapSettings.addQueryParams({ foo: 'bar' }); - loadSettings(); - assertQuery({ foo: 'bar' }); - }); - - it('merged additions with previous values', () => { - // ensure that changes are always additive - tilemapSettings.addQueryParams({ foo: 'bar' }); - tilemapSettings.addQueryParams({ bar: 'stool' }); - loadSettings(); - assertQuery({ foo: 'bar', bar: 'stool' }); - }); - - it('overwrites conflicting previous values', () => { - // ensure that conflicts are overwritten - tilemapSettings.addQueryParams({ foo: 'bar' }); - tilemapSettings.addQueryParams({ bar: 'stool' }); - tilemapSettings.addQueryParams({ foo: 'tstool' }); - loadSettings(); - assertQuery({ foo: 'tstool', bar: 'stool' }); - }); - - it('merges query params into manifest request', () => { - tilemapSettings.addQueryParams({ foo: 'bar' }); - tilemapsConfig.manifestServiceUrl = 'http://test.com/manifest?v=1'; - loadSettings('http://test.com/manifest?v=1&my_app_version=1.2.3&foo=bar'); - }); - - }); - -}); diff --git a/src/core_plugins/tile_map/public/editors/tile_map.html b/src/core_plugins/tile_map/public/editors/tile_map.html index b215b68bb0e188..e4201184dde1e8 100644 --- a/src/core_plugins/tile_map/public/editors/tile_map.html +++ b/src/core_plugins/tile_map/public/editors/tile_map.html @@ -4,8 +4,8 @@
diff --git a/src/core_plugins/tile_map/public/kibana_map.js b/src/core_plugins/tile_map/public/kibana_map.js index da48554c5220ef..e657deff532eb9 100644 --- a/src/core_plugins/tile_map/public/kibana_map.js +++ b/src/core_plugins/tile_map/public/kibana_map.js @@ -101,7 +101,7 @@ export class KibanaMap extends EventEmitter { minZoom: options.minZoom, maxZoom: options.maxZoom, center: options.center ? options.center : [0, 0], - zoom: options.zoom ? options.zoom : 0 + zoom: options.zoom ? options.zoom : 2 }; this._leafletMap = L.map(containerNode, leafletOptions); diff --git a/src/ui/public/vis_maps/lib/service_settings.js b/src/core_plugins/tile_map/public/lib/service_settings.js similarity index 100% rename from src/ui/public/vis_maps/lib/service_settings.js rename to src/core_plugins/tile_map/public/lib/service_settings.js diff --git a/src/core_plugins/tile_map/public/lib/tilemap_settings.js b/src/core_plugins/tile_map/public/lib/tilemap_settings.js deleted file mode 100644 index 54571ecc0d3a5f..00000000000000 --- a/src/core_plugins/tile_map/public/lib/tilemap_settings.js +++ /dev/null @@ -1,208 +0,0 @@ -import { uiModules } from 'ui/modules'; -import _ from 'lodash'; -import marked from 'marked'; -import { modifyUrl } from 'ui/url'; - -marked.setOptions({ - gfm: true, // Github-flavored markdown - sanitize: true // Sanitize HTML tags -}); - -uiModules.get('kibana') - .service('tilemapSettings', function ($http, tilemapsConfig, $sanitize, kbnVersion) { - const attributionFromConfig = $sanitize(marked(tilemapsConfig.deprecated.config.options.attribution || '')); - const optionsFromConfig = _.assign({}, tilemapsConfig.deprecated.config.options, { attribution: attributionFromConfig }); - const extendUrl = (url, props) => ( - modifyUrl(url, parsed => _.merge(parsed, props)) - ); - - /** - * Unescape a url template that was escaped by encodeURI() so leaflet - * will be able to correctly locate the varables in the template - * @param {String} url - * @return {String} - */ - const unescapeTemplateVars = url => { - const ENCODED_TEMPLATE_VARS_RE = /%7B(\w+?)%7D/g; - return url.replace(ENCODED_TEMPLATE_VARS_RE, (total, varName) => `{${varName}}`); - }; - - class TilemapSettings { - - constructor() { - - this._queryParams = { - my_app_version: kbnVersion - }; - this._error = null; - - //initialize settings with the default of the configuration - this._url = tilemapsConfig.deprecated.config.url; - this._tmsOptions = optionsFromConfig; - - this._invalidateSettings(); - - } - - - _invalidateSettings() { - - this._settingsInitialized = false; - this._loadSettings = _.once(async() => { - - if (tilemapsConfig.deprecated.isOverridden) {//if settings are overridden, we will use those. - this._settingsInitialized = true; - } - - if (this._settingsInitialized) { - return true; - } - - return this._getTileServiceManifest(tilemapsConfig.manifestServiceUrl, this._queryParams) - .then(response => { - const service = _.get(response, 'data.services[0]'); - if (!service) { - throw new Error('Manifest response does not include sufficient service data.'); - } - - this._error = null; - this._tmsOptions = { - attribution: $sanitize(marked(service.attribution)), - minZoom: service.minZoom, - maxZoom: service.maxZoom, - subdomains: service.subdomains || [] - }; - - this._url = unescapeTemplateVars(extendUrl(service.url, { - query: this._queryParams - })); - - this._settingsInitialized = true; - }) - .catch(e => { - this._settingsInitialized = true; - - if (!e) { - e = new Error('Unkown error'); - } - - if (!(e instanceof Error)) { - e = new Error(e.data || `status ${e.statusText || e.status}`); - } - - this._error = new Error(`Could not retrieve manifest from the tile service: ${e.message}`); - }) - .then(() => { - return true; - }); - }); - } - - /** - * Must be called before getUrl/getTMSOptions/getMapOptions can be called. - */ - loadSettings() { - return this._loadSettings(); - } - - /** - * Add optional query-parameters for the request. - * These are only applied when requesting dfrom the manifest. - * - * @param additionalQueryParams - */ - addQueryParams(additionalQueryParams) { - - //check if there are any changes in the settings. - let changes = false; - for (const key in additionalQueryParams) { - if (additionalQueryParams.hasOwnProperty(key)) { - if (additionalQueryParams[key] !== this._queryParams[key]) { - changes = true; - break; - } - } - } - - if (changes) { - this._queryParams = _.assign({}, this._queryParams, additionalQueryParams); - this._invalidateSettings(); - } - - } - - /** - * Get the url of the default TMS - * @return {string} - */ - getUrl() { - if (!this._settingsInitialized) { - throw new Error('Cannot retrieve url before calling .loadSettings first'); - } - return this._url; - } - - /** - * Get the options of the default TMS - * @return {{}} - */ - getTMSOptions() { - if (!this._settingsInitialized) { - throw new Error('Cannot retrieve options before calling .loadSettings first'); - } - return this._tmsOptions; - } - - - /** - * @return {{maxZoom: (*|number), minZoom: (*|number)}} - */ - getMinMaxZoom(isWMSEnabled) { - if (isWMSEnabled) { - return { - minZoom: 0, - maxZoom: 18 - }; - } - - //Otherwise, we use the settings from the yml. - //note that it is no longer possible to only override the zoom-settings, since all options are read from the manifest - //by default. - //For a custom configuration, users will need to override tilemap.url as well. - return { - minZoom: this._tmsOptions.minZoom, - maxZoom: this._tmsOptions.maxZoom - }; - - } - - isInitialized() { - return this._settingsInitialized; - } - - - /** - * Checks if there was an error during initialization of the parameters - */ - hasError() { - return this._error !== null; - } - - getError() { - return this._error; - } - - /** - * Make this a method to allow for overrides by test code - */ - _getTileServiceManifest(manifestUrl) { - return $http({ - url: extendUrl(manifestUrl, { query: this._queryParams }), - method: 'GET' - }); - } - - } - - return new TilemapSettings(); - }); diff --git a/src/core_plugins/tile_map/public/maps_renderbot.js b/src/core_plugins/tile_map/public/maps_renderbot.js deleted file mode 100644 index e8dd48a9aaec93..00000000000000 --- a/src/core_plugins/tile_map/public/maps_renderbot.js +++ /dev/null @@ -1,258 +0,0 @@ -import $ from 'jquery'; -import _ from 'lodash'; -import { FilterBarPushFilterProvider } from 'ui/filter_bar/push_filter'; -import { KibanaMap } from './kibana_map'; -import { GeohashLayer } from './geohash_layer'; -import './lib/tilemap_settings'; -import './styles/_tilemap.less'; - - -module.exports = function MapsRenderbotFactory(Private, $injector, tilemapSettings, Notifier, courier, getAppState) { - - const notify = new Notifier({ location: 'Tilemap' }); - - class MapsRenderbot { - - constructor(vis, $el, uiState) { - this.vis = vis; - this.$el = $el; - this.uiState = uiState; - this._geohashLayer = null; - this._kibanaMap = null; - this._$container = $el; - this._kibanaMapReady = this._makeKibanaMap($el); - - this._baseLayerDirty = true; - this._dataDirty = true; - this._paramsDirty = true; - } - - resize() { - if (this._kibanaMap) { - this._kibanaMap.resize(); - } - } - - async _makeKibanaMap() { - - if (!tilemapSettings.isInitialized()) { - await tilemapSettings.loadSettings(); - } - - if (tilemapSettings.getError()) { - //Still allow the visualization to be built, but show a toast that there was a problem retrieving map settings - //Even though the basemap will not display, the user will at least still see the overlay data - notify.warning(tilemapSettings.getError().message); - } - - if (this._kibanaMap) { - this._kibanaMap.destroy(); - } - const containerElement = $(this._$container)[0]; - const options = _.clone(this._getMinMaxZoom()); - const uiState = this.vis.getUiState(); - const zoomFromUiState = parseInt(uiState.get('mapZoom')); - const centerFromUIState = uiState.get('mapCenter'); - options.zoom = !isNaN(zoomFromUiState) ? zoomFromUiState : this.vis.type.visConfig.defaults.mapZoom; - options.center = centerFromUIState ? centerFromUIState : this.vis.type.visConfig.defaults.mapCenter; - - this._kibanaMap = new KibanaMap(containerElement, options); - this._kibanaMap.addDrawControl(); - this._kibanaMap.addFitControl(); - this._kibanaMap.addLegendControl(); - this._kibanaMap.persistUiStateForVisualization(this.vis); - - let previousPrecision = this._kibanaMap.getAutoPrecision(); - let precisionChange = false; - this._kibanaMap.on('zoomchange', () => { - precisionChange = (previousPrecision !== this._kibanaMap.getAutoPrecision()); - previousPrecision = this._kibanaMap.getAutoPrecision(); - }); - this._kibanaMap.on('zoomend', () => { - - const isAutoPrecision = _.get(this._chartData, 'geohashGridAgg.params.autoPrecision', true); - if (!isAutoPrecision) { - return; - } - - this._dataDirty = true; - if (precisionChange) { - courier.fetch(); - } else { - this._recreateGeohashLayer(); - this._dataDirty = false; - this._doRenderComplete(); - } - }); - - - this._kibanaMap.on('drawCreated:rectangle', event => { - addSpatialFilter(_.get(this._chartData, 'geohashGridAgg'), 'geo_bounding_box', event.bounds); - }); - this._kibanaMap.on('drawCreated:polygon', event => { - addSpatialFilter(_.get(this._chartData, 'geohashGridAgg'), 'geo_polygon', { points: event.points }); - }); - this._kibanaMap.on('baseLayer:loaded', () => { - this._baseLayerDirty = false; - this._doRenderComplete(); - }); - this._kibanaMap.on('baseLayer:loading', () => { - this._baseLayerDirty = true; - }); - } - - _getMinMaxZoom() { - const mapParams = this._getMapsParams(); - return tilemapSettings.getMinMaxZoom(mapParams.wms.enabled); - } - - _recreateGeohashLayer() { - if (this._geohashLayer) { - this._kibanaMap.removeLayer(this._geohashLayer); - } - if (!this._geohashGeoJson) { - return; - } - const geohashOptions = this._getGeohashOptions(); - this._geohashLayer = new GeohashLayer(this._chartData.geoJson, geohashOptions, this._kibanaMap.getZoomLevel(), this._kibanaMap); - this._kibanaMap.addLayer(this._geohashLayer); - } - - - /** - * called on data change - * @param esResponse - */ - async render(esResponse) { - this._dataDirty = true; - this._kibanaMapReady.then(() => { - this._chartData = esResponse; - this._geohashGeoJson = this._chartData.geoJson; - this._recreateGeohashLayer(); - this._kibanaMap.useUiStateFromVisualization(this.vis); - this._kibanaMap.resize(); - this._dataDirty = false; - this._doRenderComplete(); - }); - } - - destroy() { - if (this._kibanaMap) { - this._kibanaMap.destroy(); - } - } - - /** - * called on options change (vis.params change) - */ - updateParams() { - - this._paramsDirty = true; - this._kibanaMapReady.then(async() => { - const mapParams = this._getMapsParams(); - const { minZoom, maxZoom } = this._getMinMaxZoom(); - - if (mapParams.wms.enabled) { - - if (maxZoom > this._kibanaMap.getMaxZoomLevel()) { - this._geohashLayer = null; - this._kibanaMapReady = this._makeKibanaMap(); - } - - this._kibanaMap.setBaseLayer({ - baseLayerType: 'wms', - options: { - minZoom: minZoom, - maxZoom: maxZoom, - url: mapParams.wms.url, - ...mapParams.wms.options - } - }); - } else { - - if (maxZoom < this._kibanaMap.getMaxZoomLevel()) { - this._geohashLayer = null; - this._kibanaMapReady = this._makeKibanaMap(); - this._kibanaMap.setZoomLevel(maxZoom); - } - - if (!tilemapSettings.hasError()) { - const url = tilemapSettings.getUrl(); - const options = tilemapSettings.getTMSOptions(); - this._kibanaMap.setBaseLayer({ - baseLayerType: 'tms', - options: { url, ...options } - }); - } - } - const geohashOptions = this._getGeohashOptions(); - if (!this._geohashLayer || !this._geohashLayer.isReusable(geohashOptions)) { - this._recreateGeohashLayer(); - } - - this._kibanaMap.setDesaturateBaseLayer(mapParams.isDesaturated); - this._kibanaMap.setShowTooltip(mapParams.addTooltip); - this._kibanaMap.setLegendPosition(mapParams.legendPosition); - - this._kibanaMap.useUiStateFromVisualization(this.vis); - this._kibanaMap.resize(); - this._paramsDirty = false; - this._doRenderComplete(); - }); - } - - _getMapsParams() { - return _.assign( - {}, - this.vis.type.visConfig.defaults, - { - type: this.vis.type.name, - hasTimeField: this.vis.indexPattern && this.vis.indexPattern.hasTimeField()// Add attribute which determines whether an index is time based or not. - }, - this.vis.params - ); - } - - _getGeohashOptions() { - const newParams = this._getMapsParams(); - return { - valueFormatter: this._chartData ? this._chartData.valueFormatter : null, - tooltipFormatter: this._chartData ? this._chartData.tooltipFormatter : null, - mapType: newParams.mapType, - heatmap: { - heatBlur: newParams.heatBlur, - heatMaxZoom: newParams.heatMaxZoom, - heatMinOpacity: newParams.heatMinOpacity, - heatRadius: newParams.heatRadius - } - }; - } - - _doRenderComplete() { - if (this._paramsDirty || this._dataDirty || this._baseLayerDirty) { - return false; - } - this.$el.trigger('renderComplete'); - return true; - } - - } - - function addSpatialFilter(agg, filterName, filterData) { - if (!agg) { - return; - } - - const indexPatternName = agg.vis.indexPattern.id; - const field = agg.fieldName(); - const filter = {}; - filter[filterName] = { ignore_unmapped: true }; - filter[filterName][field] = filterData; - - const putFilter = Private(FilterBarPushFilterProvider)(getAppState()); - return putFilter(filter, false, indexPatternName); - } - - - return MapsRenderbot; -}; diff --git a/src/core_plugins/tile_map/public/maps_visualization.js b/src/core_plugins/tile_map/public/maps_visualization.js index 0e2f52e6f2c32b..227de739b4fdaf 100644 --- a/src/core_plugins/tile_map/public/maps_visualization.js +++ b/src/core_plugins/tile_map/public/maps_visualization.js @@ -1,33 +1,281 @@ import 'ui/vislib'; import 'plugins/kbn_vislib_vis_types/controls/vislib_basic_options'; -import MapsVisTypeMapsRenderbotProvider from './maps_renderbot'; import $ from 'jquery'; +import _ from 'lodash'; +import { FilterBarPushFilterProvider } from 'ui/filter_bar/push_filter'; +import { KibanaMap } from './kibana_map'; +import { GeohashLayer } from './geohash_layer'; +import './lib/service_settings'; +import './styles/_tilemap.less'; -export function MapsVisualizationProvider(Private) { - const MapsRenderbot = Private(MapsVisTypeMapsRenderbotProvider); +export function MapsVisualizationProvider(Private, $injector, serviceSettings, Notifier, courier, getAppState) { - class MapsVisController { - constructor(el, vis) { - this.el = $(el); - this._vis = vis; - this.renderbot = new MapsRenderbot(this._vis, this.el, vis.getUiState()); + const notify = new Notifier({ location: 'Coordinate Map' }); + + class MapsVisualization { + + constructor(element, vis) { + + this.vis = vis; + this.$el = $(element); + this._$container = this.$el; + + + this._geohashLayer = null; + this._kibanaMap = null; + this._kibanaMapReady = this._makeKibanaMap(); + this._baseLayerDirty = true; + this._dataDirty = true; + this._paramsDirty = true; + this._currentParams = null; + } + + destroy() { + if (this._kibanaMap) { + this._kibanaMap.destroy(); + } } async render(esResponse) { - //todo: should notify of render-completeness, which it isn't doing correctly now - this.renderbot.render(esResponse); + + if (esResponse && typeof esResponse.geohashGridAgg === 'undefined') { + this.updateParams(); + return; + } + + //todo: do render complete! + this._dataDirty = true; + this._kibanaMapReady.then(() => { + this._chartData = esResponse; + this._geohashGeoJson = this._chartData.geoJson; + this._recreateGeohashLayer(); + this._kibanaMap.useUiStateFromVisualization(this.vis); + this._kibanaMap.resize(); + this._dataDirty = false; + this._doRenderComplete(); + }); } resize() { - this.renderbot.resize(); + if (this._kibanaMap) { + this._kibanaMap.resize(); + } } - destroy() { - this.renderbot.destroy(); + //********************************************************************************************************** + + async _makeKibanaMap() { + + try { + this._tmsService = await serviceSettings.getTMSService(); + this._tmsError = null; + } catch (e) { + this._tmsService = null; + this._tmsError = e; + notify.warning(e.message); + } + + if (this._kibanaMap) { + this._kibanaMap.destroy(); + } + const containerElement = $(this._$container)[0]; + const options = _.clone(this._getMinMaxZoom()); + const uiState = this.vis.getUiState(); + const zoomFromUiState = parseInt(uiState.get('mapZoom')); + const centerFromUIState = uiState.get('mapCenter'); + options.zoom = !isNaN(zoomFromUiState) ? zoomFromUiState : this.vis.type.visConfig.defaults.mapZoom; + options.center = centerFromUIState ? centerFromUIState : this.vis.type.visConfig.defaults.mapCenter; + + this._kibanaMap = new KibanaMap(containerElement, options); + this._kibanaMap.addDrawControl(); + this._kibanaMap.addFitControl(); + this._kibanaMap.addLegendControl(); + this._kibanaMap.persistUiStateForVisualization(this.vis); + + let previousPrecision = this._kibanaMap.getAutoPrecision(); + let precisionChange = false; + this._kibanaMap.on('zoomchange', () => { + precisionChange = (previousPrecision !== this._kibanaMap.getAutoPrecision()); + previousPrecision = this._kibanaMap.getAutoPrecision(); + }); + this._kibanaMap.on('zoomend', () => { + + const isAutoPrecision = _.get(this._chartData, 'geohashGridAgg.params.autoPrecision', true); + if (!isAutoPrecision) { + return; + } + + this._dataDirty = true; + if (precisionChange) { + courier.fetch(); + } else { + this._recreateGeohashLayer(); + this._dataDirty = false; + this._doRenderComplete(); + } + }); + + + this._kibanaMap.on('drawCreated:rectangle', event => { + addSpatialFilter(_.get(this._chartData, 'geohashGridAgg'), 'geo_bounding_box', event.bounds); + }); + this._kibanaMap.on('drawCreated:polygon', event => { + addSpatialFilter(_.get(this._chartData, 'geohashGridAgg'), 'geo_polygon', { points: event.points }); + }); + this._kibanaMap.on('baseLayer:loaded', () => { + this._baseLayerDirty = false; + this._doRenderComplete(); + }); + this._kibanaMap.on('baseLayer:loading', () => { + this._baseLayerDirty = true; + }); + } + + _getMinMaxZoom() { + const mapParams = this._getMapsParams(); + if (this._tmsError) { + return serviceSettings.getFallbackZoomSettings(mapParams.wms.enabled); + } else { + return this._tmsService.getMinMaxZoom(mapParams.wms.enabled); + } + } + + _recreateGeohashLayer() { + if (this._geohashLayer) { + this._kibanaMap.removeLayer(this._geohashLayer); + } + if (!this._geohashGeoJson) { + return; + } + const geohashOptions = this._getGeohashOptions(); + this._geohashLayer = new GeohashLayer(this._chartData.geoJson, geohashOptions, this._kibanaMap.getZoomLevel(), this._kibanaMap); + this._kibanaMap.addLayer(this._geohashLayer); + } + + + /** + * called on options change (vis.params change) + */ + async updateParams() { + + + this._paramsDirty = true; + await this._kibanaMapReady; + + const mapParams = this._getMapsParams(); + if (_.eq(this._currentParams, mapParams)) { + this._paramsDirty = false; + return; + } + + this._currentParams = mapParams; + const { minZoom, maxZoom } = this._getMinMaxZoom(); + + if (mapParams.wms.enabled) { + + if (maxZoom > this._kibanaMap.getMaxZoomLevel()) { + this._geohashLayer = null; + this._kibanaMapReady = this._makeKibanaMap(); + } + + this._kibanaMap.setBaseLayer({ + baseLayerType: 'wms', + options: { + minZoom: minZoom, + maxZoom: maxZoom, + url: mapParams.wms.url, + ...mapParams.wms.options + } + }); + } else { + + if (maxZoom < this._kibanaMap.getMaxZoomLevel()) { + this._geohashLayer = null; + this._kibanaMapReady = this._makeKibanaMap(); + this._kibanaMap.setZoomLevel(maxZoom); + } + + if (!this._tmsError) { + const url = this._tmsService.getUrl(); + const options = this._tmsService.getTMSOptions(); + this._kibanaMap.setBaseLayer({ + baseLayerType: 'tms', + options: { url, ...options } + }); + } + } + const geohashOptions = this._getGeohashOptions(); + if (!this._geohashLayer || !this._geohashLayer.isReusable(geohashOptions)) { + this._recreateGeohashLayer(); + } + + } + + _getMapsParams() { + return _.assign( + {}, + this.vis.type.visConfig.defaults, + { type: this.vis.type.name }, + this.vis.params + ); + } + + _getGeohashOptions() { + const newParams = this._getMapsParams(); + return { + valueFormatter: this._chartData ? this._chartData.valueFormatter : null, + tooltipFormatter: this._chartData ? this._chartData.tooltipFormatter : null, + mapType: newParams.mapType, + heatmap: { + heatBlur: newParams.heatBlur, + heatMaxZoom: newParams.heatMaxZoom, + heatMinOpacity: newParams.heatMinOpacity, + heatRadius: newParams.heatRadius + } + }; + } + + _doRenderComplete() { + + return; + + if (this._paramsDirty || this._dataDirty || this._baseLayerDirty) { + const mapParams = this._getMapsParams(); + this._kibanaMap.setDesaturateBaseLayer(mapParams.isDesaturated); + this._kibanaMap.setShowTooltip(mapParams.addTooltip); + this._kibanaMap.setLegendPosition(mapParams.legendPosition); + + this._kibanaMap.useUiStateFromVisualization(this.vis); + this._kibanaMap.resize(); + this._paramsDirty = false; + + + // this._doRenderComplete(); + + return; + } + this.$el.trigger('renderComplete'); } + } - return MapsVisController; + function addSpatialFilter(agg, filterName, filterData) { + if (!agg) { + return; + } + + const indexPatternName = agg.vis.indexPattern.id; + const field = agg.fieldName(); + const filter = {}; + filter[filterName] = { ignore_unmapped: true }; + filter[filterName][field] = filterData; + + const putFilter = Private(FilterBarPushFilterProvider)(getAppState()); + return putFilter(filter, false, indexPatternName); + } + + + return MapsVisualization; } diff --git a/src/core_plugins/timelion/public/vis/index.js b/src/core_plugins/timelion/public/vis/index.js index f45b7a518ed8ff..6c32a909506c6e 100644 --- a/src/core_plugins/timelion/public/vis/index.js +++ b/src/core_plugins/timelion/public/vis/index.js @@ -2,11 +2,11 @@ import { VisFactoryProvider } from 'ui/vis/vis_factory'; import { CATEGORY } from 'ui/vis/vis_category'; import image from '../images/icon-timelion.svg'; import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; -import { RequestHandlersRegistryProvider } from 'ui/registry/request_handlers'; +import { VisRequestHandlersRegistryProvider } from 'ui/registry/vis_request_handlers'; import { TimelionRequestHandlerProvider } from './timelion_request_handler'; define(function (require) { - // we also need to load the controller and used by the template + // we also need to load the controller and directive used by the template require('plugins/timelion/vis/timelion_vis_controller'); require('plugins/timelion/directives/timelion_expression_input'); @@ -15,7 +15,7 @@ define(function (require) { // register the provider with the visTypes registry so that other know it exists VisTypesRegistryProvider.register(TimelionVisProvider); - RequestHandlersRegistryProvider.register(TimelionRequestHandlerProvider); + VisRequestHandlersRegistryProvider.register(TimelionRequestHandlerProvider); function TimelionVisProvider(Private) { const VisFactory = Private(VisFactoryProvider); diff --git a/src/ui/public/agg_types/__tests__/agg_param_writer.js b/src/ui/public/agg_types/__tests__/agg_param_writer.js index ea46c558fd3ebb..ffdd9c475f37a8 100644 --- a/src/ui/public/agg_types/__tests__/agg_param_writer.js +++ b/src/ui/public/agg_types/__tests__/agg_param_writer.js @@ -3,6 +3,7 @@ import { VisProvider } from 'ui/vis'; import { AggTypesIndexProvider } from 'ui/agg_types/index'; import { VisTypesRegistryProvider } from 'ui/registry/vis_types'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; +import { VisAggConfigProvider } from 'ui/vis/agg_config'; // eslint-disable-next-line kibana-custom/no-default-export export default function AggParamWriterHelper(Private) { @@ -10,6 +11,7 @@ export default function AggParamWriterHelper(Private) { const aggTypes = Private(AggTypesIndexProvider); const visTypes = Private(VisTypesRegistryProvider); const stubbedLogstashIndexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); + const AggConfig = Private(VisAggConfigProvider); /** * Helper object for writing aggParams. Specify an aggType and it will find a vis & schema, and @@ -66,7 +68,7 @@ export default function AggParamWriterHelper(Private) { } self.vis = new Vis(self.indexPattern, { - type: self.visType + type: self.visType.name }); } @@ -79,17 +81,26 @@ export default function AggParamWriterHelper(Private) { if (self.aggType.type === 'metrics') { paramValues.field = _.sample(self.indexPattern.fields.byType.number); } else { - paramValues.field = _.sample(self.indexPattern.fields.byType.string); + const type = self.aggType.params.byName.field.filterFieldTypes || 'string'; + let field; + do { + field = _.sample(self.indexPattern.fields.byType[type]); + } while (!field.aggregatable); + paramValues.field = field.name; } } + + const agg = new AggConfig(self.vis, { + id: 1, + schema: self.visAggSchema.name, + type: self.aggType.name, + params: paramValues + }); + self.vis.setState({ type: self.vis.type.name, - aggs: [{ - type: self.aggType, - schema: self.visAggSchema, - params: paramValues - }] + aggs: [agg.toJSON()] }); const aggConfig = _.find(self.vis.aggs, function (aggConfig) { diff --git a/src/ui/public/registry/response_handlers.js b/src/ui/public/registry/response_handlers.js deleted file mode 100644 index 6cc7203f1d3c71..00000000000000 --- a/src/ui/public/registry/response_handlers.js +++ /dev/null @@ -1,6 +0,0 @@ -import { uiRegistry } from 'ui/registry/_registry'; -export const ResponseHandlersRegistryProvider = uiRegistry({ - name: 'responseHandlers', - index: ['name'], - order: ['title'] -}); diff --git a/src/ui/public/registry/editor_types.js b/src/ui/public/registry/vis_editor_types.js similarity index 52% rename from src/ui/public/registry/editor_types.js rename to src/ui/public/registry/vis_editor_types.js index 29fc1a7e152c9e..646c63dc621724 100644 --- a/src/ui/public/registry/editor_types.js +++ b/src/ui/public/registry/vis_editor_types.js @@ -1,7 +1,7 @@ import { uiRegistry } from 'ui/registry/_registry'; -export const EditorTypesRegistryProvider = uiRegistry({ - name: 'editorTypes', +export const VisEditorTypesRegistryProvider = uiRegistry({ + name: 'visEditorTypes', index: ['name'], order: ['title'] }); diff --git a/src/ui/public/registry/request_handlers.js b/src/ui/public/registry/vis_request_handlers.js similarity index 50% rename from src/ui/public/registry/request_handlers.js rename to src/ui/public/registry/vis_request_handlers.js index 85e6a85ad07fbd..bdcd55fb0cc8d6 100644 --- a/src/ui/public/registry/request_handlers.js +++ b/src/ui/public/registry/vis_request_handlers.js @@ -1,6 +1,6 @@ import { uiRegistry } from 'ui/registry/_registry'; -export const RequestHandlersRegistryProvider = uiRegistry({ - name: 'requestHandlers', +export const VisRequestHandlersRegistryProvider = uiRegistry({ + name: 'visRequestHandlers', index: ['name'], order: ['title'] }); diff --git a/src/ui/public/registry/vis_response_handlers.js b/src/ui/public/registry/vis_response_handlers.js new file mode 100644 index 00000000000000..dd1bdc3bcf627a --- /dev/null +++ b/src/ui/public/registry/vis_response_handlers.js @@ -0,0 +1,6 @@ +import { uiRegistry } from 'ui/registry/_registry'; +export const VisResponseHandlersRegistryProvider = uiRegistry({ + name: 'visResponseHandlers', + index: ['name'], + order: ['title'] +}); diff --git a/src/ui/public/vis/__tests__/_vis.js b/src/ui/public/vis/__tests__/_vis.js index 2a3c3bc4800848..cc42d661fca88d 100644 --- a/src/ui/public/vis/__tests__/_vis.js +++ b/src/ui/public/vis/__tests__/_vis.js @@ -40,10 +40,6 @@ describe('Vis Class', function () { expect(vis).to.have.property('type'); expect(vis.type).to.eql(visTypes.byName.pie); - expect(vis).to.have.property('listeners'); - expect(vis.listeners).to.have.property('click'); - expect(vis.listeners.click).to.eql(_.noop); - expect(vis).to.have.property('params'); expect(vis.params).to.have.property('isDonut', true); expect(vis).to.have.property('indexPattern', indexPattern); @@ -63,10 +59,6 @@ describe('Vis Class', function () { expect(state).to.have.property('params'); expect(state.params).to.have.property('isDonut', true); - expect(state).to.have.property('listeners'); - expect(state.listeners).to.have.property('click'); - expect(state.listeners.click).to.eql(_.noop); - expect(state).to.have.property('aggs'); expect(state.aggs).to.have.length(3); }); @@ -86,8 +78,6 @@ describe('Vis Class', function () { expect(vis.type).to.eql(visTypes.byName.histogram); expect(vis).to.have.property('aggs'); expect(vis.aggs).to.have.length(1); - expect(vis).to.have.property('listeners'); - expect(vis.listeners).to.eql({}); expect(vis).to.have.property('params'); expect(vis.params).to.have.property('addLegend', true); expect(vis.params).to.have.property('addTooltip', true); diff --git a/src/ui/public/vis/editors/default/agg_add.html b/src/ui/public/vis/editors/default/agg_add.html index 0c0b037a17062d..51e75740812246 100644 --- a/src/ui/public/vis/editors/default/agg_add.html +++ b/src/ui/public/vis/editors/default/agg_add.html @@ -21,7 +21,7 @@