Typescript embeddable factories (#27146)

* Typescript embeddable factories

* address review comments

* Format messages

* Fixes

* Fix i18n ids
This commit is contained in:
Stacey Gammon 2018-12-18 21:39:33 -05:00 committed by GitHub
parent 2371e58590
commit 3e43a65a18
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 180 additions and 90 deletions

View file

@ -84,11 +84,9 @@ export class SearchEmbeddable extends Embeddable {
$compile,
}: SearchEmbeddableConfig) {
super({
metadata: {
title: savedSearch.title,
editUrl,
indexPattern: savedSearch.searchSource.getField('index'),
},
title: savedSearch.title,
editUrl,
indexPattern: savedSearch.searchSource.getField('index'),
});
this.onEmbeddableStateChanged = onEmbeddableStateChanged;
this.savedSearch = savedSearch;

View file

@ -24,11 +24,10 @@ import { SearchEmbeddable } from './search_embeddable';
export class SearchEmbeddableFactory extends EmbeddableFactory {
constructor($compile, $rootScope, searchLoader) {
super();
super({ name: 'search' });
this.$compile = $compile;
this.searchLoader = searchLoader;
this.$rootScope = $rootScope;
this.name = 'search';
}
getEditPath(panelId) {

View file

@ -0,0 +1,50 @@
/*
* 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 { I18nProvider } from '@kbn/i18n/react';
import React from 'react';
import ReactDOM from 'react-dom';
import { Embeddable } from 'ui/embeddable';
import { DisabledLabVisualization } from './disabled_lab_visualization';
export class DisabledLabEmbeddable extends Embeddable {
private domNode?: HTMLElement;
constructor(title: string) {
super({ title });
}
public render(domNode: HTMLElement) {
if (this.metadata.title) {
this.domNode = domNode;
ReactDOM.render(
<I18nProvider>
<DisabledLabVisualization title={this.metadata.title} />
</I18nProvider>,
domNode
);
}
}
public destroy() {
if (this.domNode) {
ReactDOM.unmountComponentAtNode(this.domNode);
}
}
}

View file

@ -0,0 +1,45 @@
/*
* 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 { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
export function DisabledLabVisualization({ title }: { title: string }) {
return (
<div className="visDisabledLabVisualization">
<div
className="kuiVerticalRhythm visDisabledLabVisualization__icon kuiIcon fa-flask"
aria-hidden="true"
/>
<div className="kuiVerticalRhythm">
<FormattedMessage
id="kbn.visualize.disabledLabVisualizationTitle"
defaultMessage="{title} is a lab visualization."
values={{ title }}
/>
</div>
<div className="kuiVerticalRhythm">
<FormattedMessage
id="kbn.visualize.disabledLabVisualizationMessage"
defaultMessage="Please turn on lab-mode in the advanced settings to see lab visualizations."
/>
</div>
</div>
);
}

View file

@ -56,11 +56,9 @@ export class VisualizeEmbeddable extends Embeddable {
loader,
}: VisualizeEmbeddableConfiguration) {
super({
metadata: {
title: savedVisualization.title,
editUrl,
indexPattern: savedVisualization.vis.indexPattern,
},
title: savedVisualization.title,
editUrl,
indexPattern: savedVisualization.vis.indexPattern,
});
this.onEmbeddableStateChanged = onEmbeddableStateChanged;
this.savedVisualization = savedVisualization;

View file

@ -17,22 +17,31 @@
* under the License.
*/
import $ from 'jquery';
import { Embeddable, EmbeddableFactory } from 'ui/embeddable';
import { getVisualizeLoader } from 'ui/visualize/loader';
import { EmbeddableFactory } from 'ui/embeddable';
import { getVisualizeLoader, VisualizeLoader } from 'ui/visualize/loader';
import { VisualizeEmbeddable } from './visualize_embeddable';
import labDisabledTemplate from './visualize_lab_disabled.html';
import { Legacy } from 'kibana';
import { OnEmbeddableStateChanged } from 'ui/embeddable/embeddable_factory';
import { VisSavedObject } from 'ui/visualize/loader/types';
import { SavedVisualizations } from '../types';
import { DisabledLabEmbeddable } from './disabled_lab_embeddable';
export interface VisualizeEmbeddableInstanceConfiguration {
id: string;
}
export class VisualizeEmbeddableFactory extends EmbeddableFactory {
constructor(savedVisualizations, config) {
super();
this._config = config;
private savedVisualizations: SavedVisualizations;
private config: Legacy.KibanaConfig;
constructor(savedVisualizations: SavedVisualizations, config: Legacy.KibanaConfig) {
super({ name: 'visualization' });
this.config = config;
this.savedVisualizations = savedVisualizations;
this.name = 'visualization';
}
getEditPath(panelId) {
public getEditPath(panelId: string) {
return this.savedVisualizations.urlFor(panelId);
}
@ -44,25 +53,22 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory {
* @param {function} onEmbeddableStateChanged
* @return {Promise.<{ metadata, onContainerStateChanged, render, destroy }>}
*/
create(panelMetadata, onEmbeddableStateChanged) {
public create(
panelMetadata: VisualizeEmbeddableInstanceConfiguration,
onEmbeddableStateChanged: OnEmbeddableStateChanged
) {
const visId = panelMetadata.id;
const editUrl = this.getEditPath(visId);
const waitFor = [getVisualizeLoader(), this.savedVisualizations.get(visId)];
return Promise.all(waitFor).then(([loader, savedObject]) => {
const isLabsEnabled = this._config.get('visualize:enableLabs');
const waitFor: [Promise<VisualizeLoader>, Promise<VisSavedObject>] = [
getVisualizeLoader(),
this.savedVisualizations.get(visId),
];
return Promise.all(waitFor).then(([loader, savedObject]: [VisualizeLoader, VisSavedObject]) => {
const isLabsEnabled = this.config.get<boolean>('visualize:enableLabs');
if (!isLabsEnabled && savedObject.vis.type.stage === 'experimental') {
return new Embeddable({
metadata: {
title: savedObject.title,
},
render: domNode => {
const template = $(labDisabledTemplate);
template.find('.visDisabledLabVisualization__title').text(savedObject.title);
$(domNode).html(template);
},
});
return new DisabledLabEmbeddable(savedObject.title);
} else {
return new VisualizeEmbeddable({
onEmbeddableStateChanged,

View file

@ -17,13 +17,17 @@
* under the License.
*/
import { VisualizeEmbeddableFactory } from './visualize_embeddable_factory';
import { Legacy } from 'kibana';
import { EmbeddableFactoriesRegistryProvider } from 'ui/embeddable/embeddable_factories_registry';
import { IPrivate } from 'ui/private';
import { SavedVisualizations } from '../types';
import { VisualizeEmbeddableFactory } from './visualize_embeddable_factory';
export function visualizeEmbeddableFactoryProvider(Private) {
export function visualizeEmbeddableFactoryProvider(Private: IPrivate) {
const VisualizeEmbeddableFactoryProvider = (
savedVisualizations,
config) => {
savedVisualizations: SavedVisualizations,
config: Legacy.KibanaConfig
) => {
return new VisualizeEmbeddableFactory(savedVisualizations, config);
};
return Private(VisualizeEmbeddableFactoryProvider);

View file

@ -1,15 +0,0 @@
<div class="visDisabledLabVisualization">
<div class="kuiVerticalRhythm visDisabledLabVisualization__icon kuiIcon fa-flask" aria-hidden="true"></div>
<div
class="kuiVerticalRhythm"
><em class="visDisabledLabVisualization__title"></em>
{{ ::'kbn.visualize.embeddableVisualization.labVisualizationInfoText' | i18n: {
defaultMessage: 'is a lab visualization.',
description: 'Part of "{visTitle} is a lab visualization."' } }}
</div>
<div
class="kuiVerticalRhythm"
i18n-id="kbn.visualize.embeddableVisualization.labModeHelpText"
i18n-default-message="Please turn on lab-mode in the advanced settings to see lab visualizations."
></div>
</div>

View file

@ -0,0 +1,25 @@
/*
* 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 { VisSavedObject } from 'ui/visualize/loader/types';
export interface SavedVisualizations {
urlFor: (id: string) => string;
get: (id: string) => Promise<VisSavedObject>;
}

View file

@ -17,18 +17,9 @@
* under the License.
*/
import * as PropTypes from 'prop-types';
import { Adapters } from 'ui/inspector';
import { ContainerState } from './types';
// TODO: we'll be able to get rid of this shape once all of dashboard is typescriptified too.
export const embeddableShape = PropTypes.shape({
destroy: PropTypes.func.isRequired,
metadata: PropTypes.object.isRequired,
onContainerStateChanged: PropTypes.func.isRequired,
render: PropTypes.func.isRequired,
});
export interface EmbeddableMetadata {
// TODO: change to an array, embeddables should be able to specify multiple index patterns they use. Also
// see https://github.com/elastic/kibana/issues/19408 - this needs to be generalized to support embeddables that
@ -53,39 +44,17 @@ export interface EmbeddableMetadata {
editUrl?: string;
}
interface EmbeddableOptions {
metadata?: EmbeddableMetadata;
render?: (domNode: HTMLElement, containerState: ContainerState) => void;
destroy?: () => void;
onContainerStateChanged?: (containerState: ContainerState) => void;
reload?: () => void;
}
export abstract class Embeddable {
public readonly metadata: EmbeddableMetadata = {};
// TODO: Make title and editUrl required and move out of options parameter.
constructor(options: EmbeddableOptions = {}) {
this.metadata = options.metadata || {};
if (options.render) {
this.render = options.render;
}
if (options.destroy) {
this.destroy = options.destroy;
}
if (options.onContainerStateChanged) {
this.onContainerStateChanged = options.onContainerStateChanged;
}
if (options.reload) {
this.reload = options.reload;
}
constructor(metadata: EmbeddableMetadata = {}) {
this.metadata = metadata || {};
}
public abstract onContainerStateChanged(containerState: ContainerState): void;
public onContainerStateChanged(containerState: ContainerState): void {
return;
}
/**
* Embeddable should render itself at the given domNode.

View file

@ -26,6 +26,17 @@ export type OnEmbeddableStateChanged = (embeddableStateChanges: EmbeddableState)
* The EmbeddableFactory creates and initializes an embeddable instance
*/
export abstract class EmbeddableFactory {
public readonly name: string;
/**
*
* @param name - a unique identified for this factory, which will be used to map an embeddable spec to
* a factory that can generate an instance of it.
*/
constructor({ name }: { name: string }) {
this.name = name;
}
/**
*
* @param {{ id: string }} containerMetadata. Currently just passing in panelState but it's more than we need, so we should