Commit 33feb26f by Torkel Ödegaard

WIP: good progress on react query editor support

parent 0260c779
......@@ -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<Props, State> {
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 = '<metrics-tab />';
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<Props, State> {
}
this.props.panel.addQuery();
this.component.digest();
this.forceUpdate();
};
......@@ -190,7 +146,6 @@ export class QueriesTab extends PureComponent<Props, State> {
onAddMixedQuery = datasource => {
this.onAddQuery({ datasource: datasource.name });
this.component.digest();
this.setState({ isAddingMixed: false });
};
......@@ -218,7 +173,17 @@ export class QueriesTab extends PureComponent<Props, State> {
<>
<PanelOptionsGroup>
<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 gf-form-query-letter-cell">
......
......@@ -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<Props, State> {
element: HTMLElement;
angularQueryEditor: AngularComponent;
export class QueryEditorRow extends PureComponent<Props, State> {
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 = '<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() {
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, $
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
......
......@@ -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;
......
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
*/
......
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