Skip to content

Commit

Permalink
use embeddables in visualize editor (elastic#48744)
Browse files Browse the repository at this point in the history
  • Loading branch information
ppisljar committed Oct 29, 2019
1 parent 5f8e608 commit 7f54adb
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 152 deletions.
20 changes: 10 additions & 10 deletions src/legacy/core_plugins/kibana/public/visualize/editor/editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,6 @@
index-patterns="[indexPattern]"
></filter-bar>

<apply-filters-popover
key="applyFiltersKey"
filters="state.$newFilters"
on-cancel="onCancelApplyFilters"
on-submit="onApplyFilters"
index-patterns="indexPatterns"
></apply-filters-popover>

<div
class="euiCallOut euiCallOut--primary euiCallOut--small hide-for-sharing"
ng-if="vis.type.shouldMarkAsExperimentalInUI()"
Expand All @@ -90,7 +82,14 @@
</div>
</div>

<div class="visualize" ng-if="!chrome.getVisible()"/>
<visualization-embedded
ng-if="!chrome.getVisible()"
class="visualize"
saved-obj="savedVis"
ui-state="uiState"
time-range="timeRange"
filters="filters"
query="query"/>

<h1
class="euiScreenReaderOnly"
Expand All @@ -107,7 +106,8 @@
saved-obj="savedVis"
ui-state="uiState"
time-range="timeRange"
filters="globalFilters"
filters="filters"
query="query"
class="visEditor__content"
/>

Expand Down
41 changes: 18 additions & 23 deletions src/legacy/core_plugins/kibana/public/visualize/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { Subscription } from 'rxjs';
import { i18n } from '@kbn/i18n';
import '../saved_visualizations/saved_visualizations';
import './visualization_editor';
import './visualization';
import 'ui/vis/editors/default/sidebar';
import 'ui/visualize';
import 'ui/collapsible_sidebar';
Expand All @@ -46,7 +47,6 @@ import { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url';
import { migrateLegacyQuery } from 'ui/utils/migrate_legacy_query';
import { subscribeWithScope } from 'ui/utils/subscribe_with_scope';
import { timefilter } from 'ui/timefilter';
import { getVisualizeLoader } from '../../../../../ui/public/visualize/loader';
import { showShareContextMenu, ShareContextMenuExtensionsRegistryProvider } from 'ui/share';
import { getUnhashableStatesProvider } from 'ui/state_management/state_hashing';
import { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal';
Expand Down Expand Up @@ -141,6 +141,7 @@ function VisEditor(
AppState,
$window,
$injector,
$timeout,
indexPatterns,
kbnUrl,
redirectWhenMissing,
Expand Down Expand Up @@ -402,22 +403,21 @@ function VisEditor(
$appStatus.dirty = status.dirty || !savedVis.id;
});

$scope.$watch('state.query', (newQuery) => {
const query = migrateLegacyQuery(newQuery);
$scope.updateQueryAndFetch({ query });
$scope.$watch('state.query', (newQuery, oldQuery) => {
if (!_.isEqual(newQuery, oldQuery)) {
const query = migrateLegacyQuery(newQuery);
if (!_.isEqual(query, newQuery)) {
$state.query = query;
}
$scope.fetch();
}
});

$state.replace();

const updateTimeRange = () => {
$scope.timeRange = timefilter.getTime();
// In case we are running in embedded mode (i.e. we used the visualize loader to embed)
// the visualization, we need to update the timeRange on the visualize handler.
if ($scope._handler) {
$scope._handler.update({
timeRange: $scope.timeRange,
});
}
$scope.$broadcast('render');
};

const subscriptions = new Subscription();
Expand All @@ -434,9 +434,10 @@ function VisEditor(
// update the searchSource when query updates
$scope.fetch = function () {
$state.save();
$scope.query = $state.query;
savedVis.searchSource.setField('query', $state.query);
savedVis.searchSource.setField('filter', $state.filters);
$scope.vis.forceReload();
$scope.$broadcast('render');
};

// update the searchSource when filters update
Expand All @@ -459,16 +460,8 @@ function VisEditor(
subscriptions.unsubscribe();
});

if (!$scope.chrome.getVisible()) {
getVisualizeLoader().then(loader => {
$scope._handler = loader.embedVisualizationWithSavedObject($element.find('.visualize')[0], savedVis, {
timeRange: $scope.timeRange,
uiState: $scope.uiState,
appState: $state,
listenOnChange: false
});
});
}

$timeout(() => { $scope.$broadcast('render'); });
}

$scope.updateQueryAndFetch = function ({ query, dateRange }) {
Expand All @@ -481,7 +474,9 @@ function VisEditor(
timefilter.setTime(dateRange);

// If nothing has changed, trigger the fetch manually, otherwise it will happen as a result of the changes
if (!isUpdate) $scope.fetch();
if (!isUpdate) {
$scope.vis.forceReload();
}
};

$scope.onRefreshChange = function ({ isPaused, refreshInterval }) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { uiModules } from 'ui/modules';
import 'angular-sanitize';
import { start as embeddables } from '../../../../../core_plugins/embeddable_api/public/np_ready/public/legacy';

uiModules
.get('kibana/directive', ['ngSanitize'])
.directive('visualizationEmbedded', function (Private, $timeout, getAppState) {

return {
restrict: 'E',
scope: {
savedObj: '=',
uiState: '=?',
timeRange: '=',
filters: '=',
query: '=',
},
link: function ($scope, element) {
$scope.renderFunction = async () => {
if (!$scope._handler) {
$scope._handler = await embeddables.getEmbeddableFactory('visualization').createFromObject($scope.savedObj, {
timeRange: $scope.timeRange,
filters: $scope.filters || [],
query: $scope.query,
appState: getAppState(),
uiState: $scope.uiState,
});
$scope._handler.render(element[0]);

} else {
$scope._handler.updateInput({
timeRange: $scope.timeRange,
filters: $scope.filters || [],
query: $scope.query,
});
}
};

$scope.$on('render', (event) => {
event.preventDefault();
$timeout(() => { $scope.renderFunction(); });
});

$scope.$on('$destroy', () => {
$scope._handler.destroy();
});
}
};
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
* under the License.
*/

import { debounce } from 'lodash';
import { uiModules } from 'ui/modules';
import 'angular-sanitize';
import { VisEditorTypesRegistryProvider } from 'ui/registry/vis_editor_types';
Expand All @@ -34,6 +33,7 @@ uiModules
uiState: '=?',
timeRange: '=',
filters: '=',
query: '=',
},
link: function ($scope, element) {
const editorType = $scope.savedObj.vis.type.editor;
Expand All @@ -46,6 +46,7 @@ uiModules
uiState: $scope.uiState,
timeRange: $scope.timeRange,
filters: $scope.filters,
query: $scope.query,
appState: getAppState(),
});
};
Expand All @@ -58,10 +59,6 @@ uiModules
$scope.$on('$destroy', () => {
editor.destroy();
});

$scope.$watchGroup(['timeRange', 'filters'], debounce(() => {
$scope.renderFunction();
}, 100));
}
};
});
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { StaticIndexPattern } from 'ui/index_patterns';
import { PersistedState } from 'ui/persisted_state';
import { VisualizeLoader } from 'ui/visualize/loader';
import { EmbeddedVisualizeHandler } from 'ui/visualize/loader/embedded_visualize_handler';
import { AppState } from 'ui/state_management/app_state';
import {
VisSavedObject,
VisualizeLoaderParams,
Expand All @@ -48,6 +49,8 @@ export interface VisualizeEmbeddableConfiguration {
editUrl: string;
loader: VisualizeLoader;
editable: boolean;
appState?: AppState;
uiState?: PersistedState;
}

export interface VisualizeInput extends EmbeddableInput {
Expand All @@ -57,6 +60,8 @@ export interface VisualizeInput extends EmbeddableInput {
vis?: {
colors?: { [key: string]: string };
};
appState?: AppState;
uiState?: PersistedState;
}

export interface VisualizeOutput extends EmbeddableOutput {
Expand All @@ -69,6 +74,7 @@ export interface VisualizeOutput extends EmbeddableOutput {
export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOutput> {
private savedVisualization: VisSavedObject;
private loader: VisualizeLoader;
private appState: AppState | undefined;
private uiState: PersistedState;
private handler?: EmbeddedVisualizeHandler;
private timeRange?: TimeRange;
Expand All @@ -86,6 +92,8 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
editUrl,
indexPatterns,
editable,
appState,
uiState,
}: VisualizeEmbeddableConfiguration,
initialInput: VisualizeInput,
parent?: Container
Expand All @@ -105,12 +113,18 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
this.savedVisualization = savedVisualization;
this.loader = loader;

const parsedUiState = savedVisualization.uiStateJSON
? JSON.parse(savedVisualization.uiStateJSON)
: {};
this.uiState = new PersistedState(parsedUiState);
if (uiState) {
this.uiState = uiState;
} else {
const parsedUiState = savedVisualization.uiStateJSON
? JSON.parse(savedVisualization.uiStateJSON)
: {};
this.uiState = new PersistedState(parsedUiState);

this.uiState.on('change', this.uiStateChangeHandler);
}

this.uiState.on('change', this.uiStateChangeHandler);
this.appState = appState;

this.subscription = Rx.merge(this.getOutput$(), this.getInput$()).subscribe(() => {
this.handleChanges();
Expand Down Expand Up @@ -149,7 +163,7 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
});
this.uiState.on('change', this.uiStateChangeHandler);
}
} else {
} else if (!this.appState) {
this.uiState.clearAllKeys();
}
}
Expand Down Expand Up @@ -211,6 +225,7 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
}

const handlerParams: VisualizeLoaderParams = {
appState: this.appState,
uiState: this.uiState,
// Append visualization to container instead of replacing its content
append: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import { getIndexPattern } from './get_index_pattern';
import { VisualizeEmbeddable, VisualizeInput, VisualizeOutput } from './visualize_embeddable';
import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
import { TypesStart } from '../../../../visualizations/public/np_ready/public/types';
import { VisSavedObject } from '../../../../../ui/public/visualize/loader/types';

interface VisualizationAttributes extends SavedObjectAttributes {
visState: string;
Expand Down Expand Up @@ -130,8 +131,8 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
});
}

public async createFromSavedObject(
savedObjectId: string,
public async createFromObject(
savedObject: VisSavedObject,
input: Partial<VisualizeInput> & { id: string },
parent?: Container
): Promise<VisualizeEmbeddable | ErrorEmbeddable | DisabledLabEmbeddable> {
Expand All @@ -140,11 +141,12 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
const savedVisualizations = $injector.get<SavedVisualizations>('savedVisualizations');

try {
const visId = savedObjectId;
const visId = savedObject.id as string;

const editUrl = chrome.addBasePath(`/app/kibana${savedVisualizations.urlFor(visId)}`);
const editUrl = visId
? chrome.addBasePath(`/app/kibana${savedVisualizations.urlFor(visId)}`)
: '';
const loader = await getVisualizeLoader();
const savedObject = await savedVisualizations.get(visId);
const isLabsEnabled = config.get<boolean>('visualize:enableLabs');

if (!isLabsEnabled && savedObject.vis.type.stage === 'experimental') {
Expand All @@ -160,6 +162,8 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
indexPatterns,
editUrl,
editable: this.isEditable(),
appState: input.appState,
uiState: input.uiState,
},
input,
parent
Expand All @@ -170,6 +174,25 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
}
}

public async createFromSavedObject(
savedObjectId: string,
input: Partial<VisualizeInput> & { id: string },
parent?: Container
): Promise<VisualizeEmbeddable | ErrorEmbeddable | DisabledLabEmbeddable> {
const $injector = await chrome.dangerouslyGetActiveInjector();
const savedVisualizations = $injector.get<SavedVisualizations>('savedVisualizations');

try {
const visId = savedObjectId;

const savedObject = await savedVisualizations.get(visId);
return this.createFromObject(savedObject, input, parent);
} catch (e) {
console.error(e); // eslint-disable-line no-console
return new ErrorEmbeddable(e, input, parent);
}
}

public async create() {
// TODO: This is a bit of a hack to preserve the original functionality. Ideally we will clean this up
// to allow for in place creation of visualizations without having to navigate away to a new URL.
Expand Down
Loading

0 comments on commit 7f54adb

Please sign in to comment.