Commit 33feb26f by Torkel Ödegaard

WIP: good progress on react query editor support

parent 0260c779
...@@ -3,18 +3,16 @@ import React, { PureComponent } from 'react'; ...@@ -3,18 +3,16 @@ import React, { PureComponent } from 'react';
import _ from 'lodash'; import _ from 'lodash';
// Components // Components
import 'app/features/panel/metrics_tab';
import { EditorTabBody, EditorToolbarView } from './EditorTabBody'; import { EditorTabBody, EditorToolbarView } from './EditorTabBody';
import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker'; import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
import { QueryInspector } from './QueryInspector'; import { QueryInspector } from './QueryInspector';
import { QueryOptions } from './QueryOptions'; import { QueryOptions } from './QueryOptions';
import { AngularQueryComponentScope } from 'app/features/panel/metrics_tab';
import { PanelOptionsGroup } from '@grafana/ui'; import { PanelOptionsGroup } from '@grafana/ui';
import { QueryEditorRow } from './QueryEditorRow';
// Services // Services
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { BackendSrv, getBackendSrv } from 'app/core/services/backend_srv'; import { BackendSrv, getBackendSrv } from 'app/core/services/backend_srv';
import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader';
import config from 'app/core/config'; import config from 'app/core/config';
// Types // Types
...@@ -37,63 +35,22 @@ interface State { ...@@ -37,63 +35,22 @@ interface State {
} }
export class QueriesTab extends PureComponent<Props, State> { export class QueriesTab extends PureComponent<Props, State> {
element: HTMLElement;
component: AngularComponent;
datasources: DataSourceSelectItem[] = getDatasourceSrv().getMetricSources(); datasources: DataSourceSelectItem[] = getDatasourceSrv().getMetricSources();
backendSrv: BackendSrv = getBackendSrv(); backendSrv: BackendSrv = getBackendSrv();
constructor(props) { state: State = {
super(props); isLoadingHelp: false,
currentDS: this.findCurrentDataSource(),
this.state = { helpContent: null,
isLoadingHelp: false, isPickerOpen: false,
currentDS: this.findCurrentDataSource(), isAddingMixed: false,
helpContent: null, };
isPickerOpen: false,
isAddingMixed: false,
};
}
findCurrentDataSource(): DataSourceSelectItem { findCurrentDataSource(): DataSourceSelectItem {
const { panel } = this.props; const { panel } = this.props;
return this.datasources.find(datasource => datasource.value === panel.datasource) || this.datasources[0]; 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 = '<metrics-tab />';
const scopeProps = {
ctrl: this.getAngularQueryComponentScope(),
};
this.component = loader.load(this.element, scopeProps, template);
}
componentWillUnmount() {
if (this.component) {
this.component.destroy();
}
}
onChangeDataSource = datasource => { onChangeDataSource = datasource => {
const { panel } = this.props; const { panel } = this.props;
const { currentDS } = this.state; const { currentDS } = this.state;
...@@ -147,7 +104,6 @@ export class QueriesTab extends PureComponent<Props, State> { ...@@ -147,7 +104,6 @@ export class QueriesTab extends PureComponent<Props, State> {
} }
this.props.panel.addQuery(); this.props.panel.addQuery();
this.component.digest();
this.forceUpdate(); this.forceUpdate();
}; };
...@@ -190,7 +146,6 @@ export class QueriesTab extends PureComponent<Props, State> { ...@@ -190,7 +146,6 @@ export class QueriesTab extends PureComponent<Props, State> {
onAddMixedQuery = datasource => { onAddMixedQuery = datasource => {
this.onAddQuery({ datasource: datasource.name }); this.onAddQuery({ datasource: datasource.name });
this.component.digest();
this.setState({ isAddingMixed: false }); this.setState({ isAddingMixed: false });
}; };
...@@ -218,7 +173,17 @@ export class QueriesTab extends PureComponent<Props, State> { ...@@ -218,7 +173,17 @@ export class QueriesTab extends PureComponent<Props, State> {
<> <>
<PanelOptionsGroup> <PanelOptionsGroup>
<div className="query-editor-rows"> <div className="query-editor-rows">
<div ref={element => (this.element = element)} /> {panel.targets.map((query, index) => (
<QueryEditorRow
datasourceName={query.datasource || panel.datasource}
key={query.refId}
panel={panel}
query={query}
onRemoveQuery={this.onRemoveQuery}
onAddQuery={this.onAddQuery}
onMoveQuery={this.onMoveQuery}
/>
))}
<div className="gf-form-query"> <div className="gf-form-query">
<div className="gf-form gf-form-query-letter-cell"> <div className="gf-form gf-form-query-letter-cell">
......
...@@ -2,29 +2,121 @@ ...@@ -2,29 +2,121 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
// Utils & Services // Utils & Services
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader'; import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader';
import { Emitter } from 'app/core/utils/emitter';
// Types // Types
import { PanelModel } from '../panel_model'; import { PanelModel } from '../panel_model';
import { DashboardModel } from '../dashboard_model'; import { DataQuery, DataSourceApi } from 'app/types/series';
interface Props { interface Props {
panel: PanelModel; 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 { interface State {
datasource: DataSourceApi | null;
} }
export class VisualizationTab extends PureComponent<Props, State> { export class QueryEditorRow extends PureComponent<Props, State> {
element: HTMLElement; element: HTMLElement | null = null;
angularQueryEditor: AngularComponent; angularQueryEditor: AngularComponent | null = null;
constructor(props) { state: State = {
super(props); 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 = '<plugin-component type="query-ctrl" />';
const scopeProps = { ctrl: this.getAngularQueryComponentScope() };
this.angularQueryEditor = loader.load(this.element, scopeProps, template);
}
componentWillUnmount() {
if (this.angularQueryEditor) {
this.angularQueryEditor.destroy();
}
} }
render() { render() {
const { datasource } = this.state;
if (!datasource) {
return null;
}
if (datasource.pluginExports.QueryCtrl) {
return <div ref={element => (this.element = element)} />;
} else if (datasource.pluginExports.QueryEditor) {
const QueryEditor = datasource.pluginExports.QueryEditor;
return <QueryEditor />;
}
return <div>Data source plugin does not export any Query Editor component</div>;
} }
} }
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;
}
// 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);
<div ng-repeat="target in ctrl.panel.targets" ng-class="{'gf-form-disabled': target.hide}">
<rebuild-on-change property="ctrl.panel.datasource || target.datasource" show-null="true">
<plugin-component type="query-ctrl">
</plugin-component>
</rebuild-on-change>
</div>
<!-- <div class="gf&#45;form&#45;query"> -->
<!-- <div class="gf&#45;form gf&#45;form&#45;query&#45;letter&#45;cell"> -->
<!-- <label class="gf&#45;form&#45;label"> -->
<!-- <span class="gf&#45;form&#45;query&#45;letter&#45;cell&#45;carret"> -->
<!-- <i class="fa fa&#45;caret&#45;down"></i> -->
<!-- </span> -->
<!-- <span class="gf&#45;form&#45;query&#45;letter&#45;cell&#45;letter">{{ctrl.nextRefId}}</span> -->
<!-- </label> -->
<!-- <button class="btn btn&#45;secondary gf&#45;form&#45;btn" ng&#45;click="ctrl.addQuery()" ng&#45;hide="ctrl.datasourceInstance.meta.mixed"> -->
<!-- Add Query -->
<!-- </button> -->
<!-- <div class="dropdown" ng&#45;if="ctrl.datasourceInstance.meta.mixed"> -->
<!-- <gf&#45;form&#45;dropdown model="ctrl.addQueryDropdown" get&#45;options="ctrl.getOptions(false)" on&#45;change="ctrl.addMixedQuery($option)"> -->
<!-- </gf&#45;form&#45;dropdown> -->
<!-- </div> -->
<!-- </div> -->
<!-- </div> -->
...@@ -105,23 +105,17 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ ...@@ -105,23 +105,17 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
switch (attrs.type) { switch (attrs.type) {
// QueryCtrl // QueryCtrl
case 'query-ctrl': { case 'query-ctrl': {
const datasource = scope.target.datasource || scope.ctrl.panel.datasource; const ds = scope.ctrl.datasource;
return datasourceSrv.get(datasource).then(ds => { return $q.when({
scope.datasource = ds; baseUrl: ds.meta.baseUrl,
name: 'query-ctrl-' + ds.meta.id,
return importPluginModule(ds.meta.module).then(dsModule => { bindings: { target: '=', panelCtrl: '=', datasource: '=' },
return { attrs: {
baseUrl: ds.meta.baseUrl, target: 'ctrl.target',
name: 'query-ctrl-' + ds.meta.id, 'panel-ctrl': 'ctrl',
bindings: { target: '=', panelCtrl: '=', datasource: '=' }, datasource: 'ctrl.datasource',
attrs: { },
target: 'target', Component: ds.pluginExports.QueryCtrl,
'panel-ctrl': 'ctrl',
datasource: 'datasource',
},
Component: dsModule.QueryCtrl,
};
});
}); });
} }
// Annotations // Annotations
......
...@@ -4,6 +4,7 @@ import { PanelProps, PanelOptionsProps } from '@grafana/ui'; ...@@ -4,6 +4,7 @@ import { PanelProps, PanelOptionsProps } from '@grafana/ui';
export interface PluginExports { export interface PluginExports {
Datasource?: any; Datasource?: any;
QueryCtrl?: any; QueryCtrl?: any;
QueryEditor?: any;
ConfigCtrl?: any; ConfigCtrl?: any;
AnnotationsQueryCtrl?: any; AnnotationsQueryCtrl?: any;
VariableQueryEditor?: any; VariableQueryEditor?: any;
......
import { PluginMeta } from './plugins'; import { PluginMeta, PluginExports } from './plugins';
import { TimeSeries, TimeRange, RawTimeRange } from '@grafana/ui'; import { TimeSeries, TimeRange, RawTimeRange } from '@grafana/ui';
export interface DataQueryResponse { export interface DataQueryResponse {
...@@ -25,6 +25,10 @@ export interface DataQueryOptions { ...@@ -25,6 +25,10 @@ export interface DataQueryOptions {
} }
export interface DataSourceApi { export interface DataSourceApi {
name: string;
meta: PluginMeta;
pluginExports: PluginExports;
/** /**
* min interval range * min interval range
*/ */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment