From 16fc1cf3d928712440bebbb7a0360f7649faac07 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Tue, 5 Jun 2018 13:58:54 -0400 Subject: [PATCH] Typescriptify ui/embeddable folder (#19648) * Typescriptify ui/embeddable folder * Allow ts modules from ui/public folders to be imported * Address review comments from Tim * Address code review from Oleg * remove lodash usage * remove mappng to non public folder --- package.json | 1 + .../discover/embeddable/search_embeddable.js | 17 ++-- .../embeddable/visualize_embeddable.js | 17 ++-- src/ui/public/embeddable/embeddable.js | 76 --------------- src/ui/public/embeddable/embeddable.ts | 95 +++++++++++++++++++ ...ry.js => embeddable_factories_registry.ts} | 4 +- ...dable_factory.js => embeddable_factory.ts} | 25 ++--- .../public/embeddable/{index.js => index.ts} | 4 +- src/ui/public/embeddable/types.ts | 57 +++++++++++ tsconfig.json | 4 +- yarn.lock | 4 + 11 files changed, 187 insertions(+), 117 deletions(-) delete mode 100644 src/ui/public/embeddable/embeddable.js create mode 100644 src/ui/public/embeddable/embeddable.ts rename src/ui/public/embeddable/{embeddable_factories_registry.js => embeddable_factories_registry.ts} (94%) rename src/ui/public/embeddable/{embeddable_factory.js => embeddable_factory.ts} (66%) rename src/ui/public/embeddable/{index.js => index.ts} (90%) create mode 100644 src/ui/public/embeddable/types.ts diff --git a/package.json b/package.json index ae6a38a620fa..d2f0d5195166 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "@kbn/pm": "link:packages/kbn-pm", "@kbn/test-subj-selector": "link:packages/kbn-test-subj-selector", "@kbn/ui-framework": "link:packages/kbn-ui-framework", + "@types/prop-types": "^15.5.3", "JSONStream": "1.1.1", "accept-language-parser": "1.2.0", "angular": "1.6.9", diff --git a/src/core_plugins/kibana/public/discover/embeddable/search_embeddable.js b/src/core_plugins/kibana/public/discover/embeddable/search_embeddable.js index 0b1b7c96765b..9129085e18f8 100644 --- a/src/core_plugins/kibana/public/discover/embeddable/search_embeddable.js +++ b/src/core_plugins/kibana/public/discover/embeddable/search_embeddable.js @@ -24,22 +24,19 @@ import * as columnActions from 'ui/doc_table/actions/columns'; export class SearchEmbeddable extends Embeddable { constructor({ onEmbeddableStateChanged, savedSearch, editUrl, loader, $rootScope, $compile }) { - super(); + super({ + metadata: { + title: savedSearch.title, + editUrl, + indexPattern: savedSearch.searchSource.get('index') + } + }); this.onEmbeddableStateChanged = onEmbeddableStateChanged; this.savedSearch = savedSearch; this.loader = loader; this.$rootScope = $rootScope; this.$compile = $compile; this.customization = {}; - - /** - * @type {EmbeddableMetadata} - */ - this.metadata = { - title: savedSearch.title, - editUrl, - indexPattern: this.savedSearch.searchSource.get('index'), - }; } emitEmbeddableStateChange(embeddableState) { diff --git a/src/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.js b/src/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.js index dc4ba34c3824..71a63dc5ac67 100644 --- a/src/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.js +++ b/src/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.js @@ -24,7 +24,13 @@ import _ from 'lodash'; export class VisualizeEmbeddable extends Embeddable { constructor({ onEmbeddableStateChanged, savedVisualization, editUrl, loader }) { - super(); + super({ + metadata: { + title: savedVisualization.title, + editUrl, + indexPattern: savedVisualization.vis.indexPattern + } + }); this._onEmbeddableStateChanged = onEmbeddableStateChanged; this.savedVisualization = savedVisualization; this.loader = loader; @@ -33,15 +39,6 @@ export class VisualizeEmbeddable extends Embeddable { this.uiState = new PersistedState(parsedUiState); this.uiState.on('change', this._uiStateChangeHandler); - - /** - * @type {EmbeddableMetadata} - */ - this.metadata = { - title: savedVisualization.title, - editUrl, - indexPattern: this.savedVisualization.vis.indexPattern - }; } _uiStateChangeHandler = () => { diff --git a/src/ui/public/embeddable/embeddable.js b/src/ui/public/embeddable/embeddable.js deleted file mode 100644 index a98c41b13573..000000000000 --- a/src/ui/public/embeddable/embeddable.js +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 { PropTypes } from 'prop-types'; - -/** - * @typedef {Object} EmbeddableMetadata - data that does not change over the course of the embeddables life span. - * @property {string} title - * @property {string|undefined} editUrl - * @property {IndexPattern} indexPattern - */ - -export const embeddableShape = PropTypes.shape({ - metadata: PropTypes.object.isRequired, - onContainerStateChanged: PropTypes.func.isRequired, - render: PropTypes.func.isRequired, - destroy: PropTypes.func.isRequired, -}); - -export class Embeddable { - /** - * - * @param {Object|undefined} config - * @param {EmbeddableMetadata|undefined} config.metadata optional metadata - * @param {function|undefined} config.render optional render method - * @param {function|undefined} config.destroy optional destroy method - * @param {function|undefined} config.onContainerStateChanged optional onContainerStateChanged method - */ - constructor(config = {}) { - /** - * @type {EmbeddableMetadata} - */ - this.metadata = config.metadata || {}; - - if (config.render) { - this.render = config.render; - } - - if (config.destroy) { - this.destroy = config.destroy; - } - - if (config.onContainerStateChanged) { - this.onContainerStateChanged = config.onContainerStateChanged; - } - } - - /** - * @param {ContainerState} containerState - */ - onContainerStateChanged(/*containerState*/) {} - - /** - * @param {Element} domNode - the dom node to mount the rendered embeddable on - * @param {ContainerState} containerState - */ - render(/*domNode, containerState*/) {} - - destroy() {} -} diff --git a/src/ui/public/embeddable/embeddable.ts b/src/ui/public/embeddable/embeddable.ts new file mode 100644 index 000000000000..c977a74b176b --- /dev/null +++ b/src/ui/public/embeddable/embeddable.ts @@ -0,0 +1,95 @@ +/* + * 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 * as PropTypes from 'prop-types'; +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, +}); + +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 + // use dynamic index patterns (vega, TSVB) instead of saved object index patterns (most other visualizations). + /** + * Should specify any index pattern the embeddable uses. This will be used by the container to list out + * available fields to filter on. + */ + indexPattern?: object; + + /** + * The title, or name, of the embeddable. + */ + title?: string; + + /** + * A url to direct the user for managing the embeddable instance. We may want to eventually make this optional + * for non-instanced panels that can only be created and deleted but not edited. We also wish to eventually support + * in-place editing on the dashboard itself, so another option could be to supply an element, or fly out panel, to + * offer for editing directly on the dashboard. + */ + editUrl?: string; +} + +interface EmbeddableOptions { + metadata?: EmbeddableMetadata; + render?: (domNode: HTMLElement, containerState: ContainerState) => void; + destroy?: () => void; + onContainerStateChanged?: (containerState: ContainerState) => 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; + } + } + + public abstract onContainerStateChanged(containerState: ContainerState): void; + + /** + * Embeddable should render itself at the given domNode. + */ + public abstract render( + domNode: HTMLElement, + containerState: ContainerState + ): void; + + public destroy(): void { + return; + } +} diff --git a/src/ui/public/embeddable/embeddable_factories_registry.js b/src/ui/public/embeddable/embeddable_factories_registry.ts similarity index 94% rename from src/ui/public/embeddable/embeddable_factories_registry.js rename to src/ui/public/embeddable/embeddable_factories_registry.ts index 7f69625b0224..8b227081ec6f 100644 --- a/src/ui/public/embeddable/embeddable_factories_registry.js +++ b/src/ui/public/embeddable/embeddable_factories_registry.ts @@ -17,13 +17,13 @@ * under the License. */ +// @ts-ignore: implicit any for JS file import { uiRegistry } from '../registry/_registry'; /** * Registry of functions (EmbeddableFactoryProviders) which return an EmbeddableFactory. */ export const EmbeddableFactoriesRegistryProvider = uiRegistry({ + index: ['name'], name: 'embeddableFactories', - index: ['name'] }); - diff --git a/src/ui/public/embeddable/embeddable_factory.js b/src/ui/public/embeddable/embeddable_factory.ts similarity index 66% rename from src/ui/public/embeddable/embeddable_factory.js rename to src/ui/public/embeddable/embeddable_factory.ts index bda401b62c26..09a2c7535c36 100644 --- a/src/ui/public/embeddable/embeddable_factory.js +++ b/src/ui/public/embeddable/embeddable_factory.ts @@ -17,33 +17,24 @@ * under the License. */ -/** - * @typedef {Object} EmbeddableState - * @property {Object} customization - any customization data that should be stored at the panel level. For - * example, pie slice colors, or custom per panel sort order or columns. - * @property {Object} stagedFilter - a possible filter the embeddable wishes dashboard to apply. - */ - - -/** - * @callback onEmbeddableStateChanged - * @param {EmbeddableState} embeddableState - */ +import { Embeddable } from './embeddable'; +import { EmbeddableState } from './types'; /** * The EmbeddableFactory creates and initializes an embeddable instance */ -export class EmbeddableFactory { +export abstract class EmbeddableFactory { /** * - * @param {Object} containerMetadata. Currently just passing in panelState but it's more than we need, so we should + * @param {{ id: string }} containerMetadata. Currently just passing in panelState but it's more than we need, so we should * decouple this to only include data given to us from the embeddable when it's added to the dashboard. Generally * will be just the object id, but could be anything depending on the plugin. * @param {onEmbeddableStateChanged} onEmbeddableStateChanged - embeddable should call this function with updated * state whenever something changes that the dashboard should know about. * @return {Promise.} */ - create(/* containerMetadata, onEmbeddableStateChanged*/) { - throw new Error('Must implement create.'); - } + public abstract create( + containerMetadata: { id: string }, + onEmbeddableStateChanged: (embeddableStateChanges: EmbeddableState) => void + ): Promise; } diff --git a/src/ui/public/embeddable/index.js b/src/ui/public/embeddable/index.ts similarity index 90% rename from src/ui/public/embeddable/index.js rename to src/ui/public/embeddable/index.ts index 164d6a4f9252..5517a14efede 100644 --- a/src/ui/public/embeddable/index.js +++ b/src/ui/public/embeddable/index.ts @@ -19,4 +19,6 @@ export { EmbeddableFactory } from './embeddable_factory'; export * from './embeddable'; -export { EmbeddableFactoriesRegistryProvider } from './embeddable_factories_registry'; +export { + EmbeddableFactoriesRegistryProvider, +} from './embeddable_factories_registry'; diff --git a/src/ui/public/embeddable/types.ts b/src/ui/public/embeddable/types.ts new file mode 100644 index 000000000000..0f1e726e9429 --- /dev/null +++ b/src/ui/public/embeddable/types.ts @@ -0,0 +1,57 @@ +/* + * 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. + */ + +export interface ContainerState { + // 'view' or 'edit'. Should probably be an enum but I'm undecided where to define it, here or in dashboard code. + viewMode: string; + + timeRange: { + // To and From should be either an absolute time range in utc format or a relative one (e.g. now-15m) + to: string; + from: string; + }; + + // The shape will be up to the embeddable type. + embeddableCustomization?: object; + + /** + * Whether or not panel titles are hidden. It is not the embeddable's responsibility to hide the title (the container + * handles that). This information is currently only used to determine the title for reporting (data-sharing-title + * attribute). If we move that out of the embeddables and push it to the container (as we probably should), then + * we shouldn't need to expose this information. + */ + hidePanelTitles: boolean; + + /** + * Is the current panel in expanded mode + */ + isPanelExpanded: boolean; +} + +export interface EmbeddableState { + /** + * Any customization data that should be stored at the panel level. For + * example, pie slice colors, or custom per panel sort order or columns. + */ + customization: object; + /** + * A possible filter the embeddable wishes dashboard to apply. + */ + stagedFilter: object; +} diff --git a/tsconfig.json b/tsconfig.json index eb218de056e4..750e4be86461 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,9 @@ { "compilerOptions": { "baseUrl": ".", - + "paths": { + "ui/*": ["src/ui/public/*"] + }, // Support .tsx files and transform JSX into calls to React.createElement "jsx": "react", diff --git a/yarn.lock b/yarn.lock index e1bc75895695..4b832d70c2cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -324,6 +324,10 @@ dependencies: "@types/retry" "*" +"@types/prop-types@^15.5.3": + version "15.5.3" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.3.tgz#bef071852dca2a2dbb65fecdb7bfb30cedae2de2" + "@types/react-dom@^16.0.5": version "16.0.5" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.5.tgz#a757457662e3819409229e8f86795ff37b371f96"