diff --git a/public/app/features/dashboard/panel_editor/QueriesTab.tsx b/public/app/features/dashboard/panel_editor/QueriesTab.tsx index 47c4f358136..1c842e6572c 100644 --- a/public/app/features/dashboard/panel_editor/QueriesTab.tsx +++ b/public/app/features/dashboard/panel_editor/QueriesTab.tsx @@ -3,18 +3,16 @@ import React, { PureComponent } from 'react'; import _ from 'lodash'; // Components -import 'app/features/panel/metrics_tab'; import { EditorTabBody, EditorToolbarView } from './EditorTabBody'; import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker'; import { QueryInspector } from './QueryInspector'; import { QueryOptions } from './QueryOptions'; -import { AngularQueryComponentScope } from 'app/features/panel/metrics_tab'; import { PanelOptionsGroup } from '@grafana/ui'; +import { QueryEditorRow } from './QueryEditorRow'; // Services import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { BackendSrv, getBackendSrv } from 'app/core/services/backend_srv'; -import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader'; import config from 'app/core/config'; // Types @@ -37,63 +35,22 @@ interface State { } export class QueriesTab extends PureComponent { - element: HTMLElement; - component: AngularComponent; datasources: DataSourceSelectItem[] = getDatasourceSrv().getMetricSources(); backendSrv: BackendSrv = getBackendSrv(); - constructor(props) { - super(props); - - this.state = { - isLoadingHelp: false, - currentDS: this.findCurrentDataSource(), - helpContent: null, - isPickerOpen: false, - isAddingMixed: false, - }; - } + state: State = { + isLoadingHelp: false, + currentDS: this.findCurrentDataSource(), + helpContent: null, + isPickerOpen: false, + isAddingMixed: false, + }; findCurrentDataSource(): DataSourceSelectItem { const { panel } = this.props; return this.datasources.find(datasource => datasource.value === panel.datasource) || this.datasources[0]; } - getAngularQueryComponentScope(): AngularQueryComponentScope { - const { panel, dashboard } = this.props; - - return { - panel: panel, - dashboard: dashboard, - refresh: () => panel.refresh(), - render: () => panel.render, - addQuery: this.onAddQuery, - moveQuery: this.onMoveQuery, - removeQuery: this.onRemoveQuery, - events: panel.events, - }; - } - - componentDidMount() { - if (!this.element) { - return; - } - - const loader = getAngularLoader(); - const template = ''; - const scopeProps = { - ctrl: this.getAngularQueryComponentScope(), - }; - - this.component = loader.load(this.element, scopeProps, template); - } - - componentWillUnmount() { - if (this.component) { - this.component.destroy(); - } - } - onChangeDataSource = datasource => { const { panel } = this.props; const { currentDS } = this.state; @@ -147,7 +104,6 @@ export class QueriesTab extends PureComponent { } this.props.panel.addQuery(); - this.component.digest(); this.forceUpdate(); }; @@ -190,7 +146,6 @@ export class QueriesTab extends PureComponent { onAddMixedQuery = datasource => { this.onAddQuery({ datasource: datasource.name }); - this.component.digest(); this.setState({ isAddingMixed: false }); }; @@ -218,7 +173,17 @@ export class QueriesTab extends PureComponent { <>
-
(this.element = element)} /> + {panel.targets.map((query, index) => ( + + ))}
diff --git a/public/app/features/dashboard/panel_editor/QueryEditorRow.tsx b/public/app/features/dashboard/panel_editor/QueryEditorRow.tsx index b90c11da7c3..1028815cf08 100644 --- a/public/app/features/dashboard/panel_editor/QueryEditorRow.tsx +++ b/public/app/features/dashboard/panel_editor/QueryEditorRow.tsx @@ -2,29 +2,121 @@ import React, { PureComponent } from 'react'; // Utils & Services +import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader'; +import { Emitter } from 'app/core/utils/emitter'; // Types import { PanelModel } from '../panel_model'; -import { DashboardModel } from '../dashboard_model'; +import { DataQuery, DataSourceApi } from 'app/types/series'; interface Props { panel: PanelModel; - dashboard: DashboardModel; + query: DataQuery; + onAddQuery: (query?: DataQuery) => void; + onRemoveQuery: (query: DataQuery) => void; + onMoveQuery: (query: DataQuery, direction: number) => void; + datasourceName: string | null; } interface State { + datasource: DataSourceApi | null; } -export class VisualizationTab extends PureComponent { - element: HTMLElement; - angularQueryEditor: AngularComponent; +export class QueryEditorRow extends PureComponent { + element: HTMLElement | null = null; + angularQueryEditor: AngularComponent | null = null; - constructor(props) { - super(props); + state: State = { + datasource: null, + }; + + componentDidMount() { + this.loadDatasource(); + } + + getAngularQueryComponentScope(): AngularQueryComponentScope { + const { panel, onAddQuery, onMoveQuery, onRemoveQuery, query } = this.props; + const { datasource } = this.state; + + return { + datasource: datasource, + target: query, + panel: panel, + refresh: () => panel.refresh(), + render: () => panel.render, + addQuery: onAddQuery, + moveQuery: onMoveQuery, + removeQuery: onRemoveQuery, + events: panel.events, + }; + } + + async loadDatasource() { + const { query, panel } = this.props; + const dataSourceSrv = getDatasourceSrv(); + const datasource = await dataSourceSrv.get(query.datasource || panel.datasource); + + this.setState({ datasource }); + } + + componentDidUpdate() { + const { datasource } = this.state; + + // check if we need to load another datasource + if (datasource && datasource.name !== this.props.datasourceName) { + if (this.angularQueryEditor) { + this.angularQueryEditor.destroy(); + this.angularQueryEditor = null; + } + this.loadDatasource(); + return; + } + + if (!this.element || this.angularQueryEditor) { + return; + } + + const loader = getAngularLoader(); + const template = ''; + const scopeProps = { ctrl: this.getAngularQueryComponentScope() }; + + this.angularQueryEditor = loader.load(this.element, scopeProps, template); + } + + componentWillUnmount() { + if (this.angularQueryEditor) { + this.angularQueryEditor.destroy(); + } } render() { + const { datasource } = this.state; + if (!datasource) { + return null; + } + + if (datasource.pluginExports.QueryCtrl) { + return
(this.element = element)} />; + } else if (datasource.pluginExports.QueryEditor) { + const QueryEditor = datasource.pluginExports.QueryEditor; + return ; + } + + return
Data source plugin does not export any Query Editor component
; } } + +export interface AngularQueryComponentScope { + target: DataQuery; + panel: PanelModel; + events: Emitter; + refresh: () => void; + render: () => void; + removeQuery: (query: DataQuery) => void; + addQuery: (query?: DataQuery) => void; + moveQuery: (query: DataQuery, direction: number) => void; + datasource: DataSourceApi; +} + diff --git a/public/app/features/panel/metrics_tab.ts b/public/app/features/panel/metrics_tab.ts deleted file mode 100644 index 74418484e3a..00000000000 --- a/public/app/features/panel/metrics_tab.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Services & utils -import coreModule from 'app/core/core_module'; -import { Emitter } from 'app/core/utils/emitter'; - -// Types -import { DashboardModel } from '../dashboard/dashboard_model'; -import { PanelModel } from '../dashboard/panel_model'; -import { DataQuery } from 'app/types'; - -export interface AngularQueryComponentScope { - panel: PanelModel; - dashboard: DashboardModel; - events: Emitter; - refresh: () => void; - render: () => void; - removeQuery: (query: DataQuery) => void; - addQuery: (query?: DataQuery) => void; - moveQuery: (query: DataQuery, direction: number) => void; -} - -/** @ngInject */ -export function metricsTabDirective() { - 'use strict'; - return { - restrict: 'E', - scope: true, - templateUrl: 'public/app/features/panel/partials/metrics_tab.html', - }; -} - -coreModule.directive('metricsTab', metricsTabDirective); diff --git a/public/app/features/panel/partials/metrics_tab.html b/public/app/features/panel/partials/metrics_tab.html deleted file mode 100644 index 5e9f23ba2ef..00000000000 --- a/public/app/features/panel/partials/metrics_tab.html +++ /dev/null @@ -1,24 +0,0 @@ -
- - - - -
- - - - - - - - - - - - - - - - - - diff --git a/public/app/features/plugins/plugin_component.ts b/public/app/features/plugins/plugin_component.ts index 7092608085d..0b305e05f5b 100644 --- a/public/app/features/plugins/plugin_component.ts +++ b/public/app/features/plugins/plugin_component.ts @@ -105,23 +105,17 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ switch (attrs.type) { // QueryCtrl case 'query-ctrl': { - const datasource = scope.target.datasource || scope.ctrl.panel.datasource; - return datasourceSrv.get(datasource).then(ds => { - scope.datasource = ds; - - return importPluginModule(ds.meta.module).then(dsModule => { - return { - baseUrl: ds.meta.baseUrl, - name: 'query-ctrl-' + ds.meta.id, - bindings: { target: '=', panelCtrl: '=', datasource: '=' }, - attrs: { - target: 'target', - 'panel-ctrl': 'ctrl', - datasource: 'datasource', - }, - Component: dsModule.QueryCtrl, - }; - }); + const ds = scope.ctrl.datasource; + return $q.when({ + baseUrl: ds.meta.baseUrl, + name: 'query-ctrl-' + ds.meta.id, + bindings: { target: '=', panelCtrl: '=', datasource: '=' }, + attrs: { + target: 'ctrl.target', + 'panel-ctrl': 'ctrl', + datasource: 'ctrl.datasource', + }, + Component: ds.pluginExports.QueryCtrl, }); } // Annotations diff --git a/public/app/types/plugins.ts b/public/app/types/plugins.ts index a1403c7a71c..4dacb3f8ccb 100644 --- a/public/app/types/plugins.ts +++ b/public/app/types/plugins.ts @@ -4,6 +4,7 @@ import { PanelProps, PanelOptionsProps } from '@grafana/ui'; export interface PluginExports { Datasource?: any; QueryCtrl?: any; + QueryEditor?: any; ConfigCtrl?: any; AnnotationsQueryCtrl?: any; VariableQueryEditor?: any; diff --git a/public/app/types/series.ts b/public/app/types/series.ts index 9fe68955da5..6f1795ef544 100644 --- a/public/app/types/series.ts +++ b/public/app/types/series.ts @@ -1,4 +1,4 @@ -import { PluginMeta } from './plugins'; +import { PluginMeta, PluginExports } from './plugins'; import { TimeSeries, TimeRange, RawTimeRange } from '@grafana/ui'; export interface DataQueryResponse { @@ -25,6 +25,10 @@ export interface DataQueryOptions { } export interface DataSourceApi { + name: string; + meta: PluginMeta; + pluginExports: PluginExports; + /** * min interval range */