Moved more metrics tab to react

This commit is contained in:
Torkel Ödegaard 2018-12-11 13:36:44 +01:00
parent d6c6ba642e
commit 4591c3555c
8 changed files with 136 additions and 221 deletions

View File

@ -143,7 +143,7 @@ export class DashboardMigrator {
panelUpgrades.push(panel => { panelUpgrades.push(panel => {
_.each(panel.targets, target => { _.each(panel.targets, target => {
if (!target.refId) { if (!target.refId) {
target.refId = this.dashboard.getNextQueryLetter(panel); target.refId = panel.getNextQueryLetter();
} }
}); });
}); });

View File

@ -806,16 +806,6 @@ export class DashboardModel {
return this.timezone === 'browser' ? moment(date).fromNow() : moment.utc(date).fromNow(); return this.timezone === 'browser' ? moment(date).fromNow() : moment.utc(date).fromNow();
} }
getNextQueryLetter(panel) {
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
return _.find(letters, refId => {
return _.every(panel.targets, other => {
return other.refId !== refId;
});
});
}
isTimezoneUtc() { isTimezoneUtc() {
return this.getTimezone() === 'utc'; return this.getTimezone() === 'utc';
} }

View File

@ -1,21 +1,27 @@
// Libraries
import React, { SFC, PureComponent } from 'react'; import React, { SFC, PureComponent } from 'react';
import Remarkable from 'remarkable';
import _ from 'lodash';
// Components
import DataSourceOption from './DataSourceOption'; import DataSourceOption from './DataSourceOption';
import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
import { EditorTabBody } from './EditorTabBody'; import { EditorTabBody } from './EditorTabBody';
import { DataSourcePicker } from './DataSourcePicker'; import { DataSourcePicker } from './DataSourcePicker';
import { PanelModel } from '../panel_model';
import { DashboardModel } from '../dashboard_model';
import './../../panel/metrics_tab';
import config from 'app/core/config';
import { QueryInspector } from './QueryInspector'; import { QueryInspector } from './QueryInspector';
import { TimeRangeOptions } from './TimeRangeOptions'; import { TimeRangeOptions } from './TimeRangeOptions';
import './../../panel/metrics_tab';
import { AngularQueryComponentScope } from 'app/features/panel/metrics_tab';
// Services // Services
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { getBackendSrv, BackendSrv } from 'app/core/services/backend_srv'; import { getBackendSrv, BackendSrv } from 'app/core/services/backend_srv';
import { DataSourceSelectItem } from 'app/types'; import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
import config from 'app/core/config';
import Remarkable from 'remarkable'; // Types
import { PanelModel } from '../panel_model';
import { DashboardModel } from '../dashboard_model';
import { DataSourceSelectItem, DataQuery } from 'app/types';
interface Props { interface Props {
panel: PanelModel; panel: PanelModel;
@ -40,7 +46,7 @@ interface LoadingPlaceholderProps {
const LoadingPlaceholder: SFC<LoadingPlaceholderProps> = ({ text }) => <h2>{text}</h2>; const LoadingPlaceholder: SFC<LoadingPlaceholderProps> = ({ text }) => <h2>{text}</h2>;
export class QueriesTab extends PureComponent<Props, State> { export class QueriesTab extends PureComponent<Props, State> {
element: any; element: HTMLElement;
component: AngularComponent; component: AngularComponent;
datasources: DataSourceSelectItem[] = getDatasourceSrv().getMetricSources(); datasources: DataSourceSelectItem[] = getDatasourceSrv().getMetricSources();
backendSrv: BackendSrv = getBackendSrv(); backendSrv: BackendSrv = getBackendSrv();
@ -59,20 +65,30 @@ export class QueriesTab extends PureComponent<Props, State> {
}; };
} }
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() { componentDidMount() {
if (!this.element) { if (!this.element) {
return; return;
} }
const { panel, dashboard } = this.props;
const loader = getAngularLoader(); const loader = getAngularLoader();
const template = '<metrics-tab />'; const template = '<metrics-tab />';
const scopeProps = { const scopeProps = {
ctrl: { ctrl: this.getAngularQueryComponentScope(),
panel: panel,
dashboard: dashboard,
refresh: () => panel.refresh(),
},
}; };
this.component = loader.load(this.element, scopeProps, template); this.component = loader.load(this.element, scopeProps, template);
@ -87,6 +103,7 @@ export class QueriesTab extends PureComponent<Props, State> {
onChangeDataSource = datasource => { onChangeDataSource = datasource => {
const { panel } = this.props; const { panel } = this.props;
const { currentDatasource } = this.state; const { currentDatasource } = this.state;
// switching to mixed // switching to mixed
if (datasource.meta.mixed) { if (datasource.meta.mixed) {
panel.targets.forEach(target => { panel.targets.forEach(target => {
@ -95,10 +112,16 @@ export class QueriesTab extends PureComponent<Props, State> {
target.datasource = config.defaultDatasource; target.datasource = config.defaultDatasource;
} }
}); });
} else if (currentDatasource && currentDatasource.meta.mixed) { } else if (currentDatasource) {
panel.targets.forEach(target => { // if switching from mixed
delete target.datasource; if (currentDatasource.meta.mixed) {
}); for (const target of panel.targets) {
delete target.datasource;
}
} else if (currentDatasource.meta.id !== datasource.meta.id) {
// we are changing data source type, clear queries
panel.targets = [{ refId: 'A' }];
}
} }
panel.datasource = datasource.value; panel.datasource = datasource.value;
@ -227,9 +250,41 @@ export class QueriesTab extends PureComponent<Props, State> {
return isLoading ? <LoadingPlaceholder text="Loading help..." /> : helpHtml; return isLoading ? <LoadingPlaceholder text="Loading help..." /> : helpHtml;
}; };
onAddQuery = (query?: DataQuery) => {
this.props.panel.addQuery(query);
this.forceUpdate();
};
onAddQueryClick = () => {
this.props.panel.addQuery();
this.component.digest();
this.forceUpdate();
};
onRemoveQuery = (query: DataQuery) => {
const { panel } = this.props;
const index = _.indexOf(panel.targets, query);
panel.targets.splice(index, 1);
panel.refresh();
this.forceUpdate();
};
onMoveQuery = (query: DataQuery, direction: number) => {
const { panel } = this.props;
const index = _.indexOf(panel.targets, query);
_.move(panel.targets, index, index + direction);
this.forceUpdate();
};
render() { render() {
const { panel } = this.props;
const { currentDatasource } = this.state; const { currentDatasource } = this.state;
const { hasQueryHelp } = currentDatasource.meta; const { hasQueryHelp } = currentDatasource.meta;
const dsInformation = { const dsInformation = {
title: currentDatasource.name, title: currentDatasource.name,
imgSrc: currentDatasource.meta.info.logos.small, imgSrc: currentDatasource.meta.info.logos.small,
@ -266,9 +321,23 @@ export class QueriesTab extends PureComponent<Props, State> {
return ( return (
<EditorTabBody heading="Queries" main={dsInformation} toolbarItems={[options, queryInspector, dsHelp]}> <EditorTabBody heading="Queries" main={dsInformation} toolbarItems={[options, queryInspector, dsHelp]}>
<> <div className="query-editor-rows gf-form-group">
<div ref={element => (this.element = element)} style={{ width: '100%' }} /> <div ref={element => (this.element = element)} />
</>
<div className="gf-form-query">
<div className="gf-form gf-form-query-letter-cell">
<label className="gf-form-label">
<span className="gf-form-query-letter-cell-carret muted">
<i className="fa fa-caret-down" />
</span>
<span className="gf-form-query-letter-cell-letter">{panel.getNextQueryLetter()}</span>
</label>
<button className="btn btn-secondary gf-form-btn" onClick={this.onAddQueryClick}>
Add Query
</button>
</div>
</div>
</div>
</EditorTabBody> </EditorTabBody>
); );
} }

View File

@ -1,6 +1,7 @@
import { Emitter } from 'app/core/utils/emitter'; import { Emitter } from 'app/core/utils/emitter';
import _ from 'lodash'; import _ from 'lodash';
import { PANEL_OPTIONS_KEY_PREFIX } from 'app/core/constants'; import { PANEL_OPTIONS_KEY_PREFIX } from 'app/core/constants';
import { DataQuery } from 'app/types';
export interface GridPos { export interface GridPos {
x: number; x: number;
@ -237,6 +238,23 @@ export class PanelModel {
this.restorePanelOptions(pluginId); this.restorePanelOptions(pluginId);
} }
addQuery(query?: DataQuery) {
query = query || { refId: 'A' };
(query.refId = this.getNextQueryLetter()), (query.isNew = true);
this.targets.push(query);
}
getNextQueryLetter(): string {
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
return _.find(letters, refId => {
return _.every(this.targets, other => {
return other.refId !== refId;
});
});
}
destroy() { destroy() {
this.events.emit('panel-teardown'); this.events.emit('panel-teardown');
this.events.removeAllListeners(); this.events.removeAllListeners();

View File

@ -1,181 +1,24 @@
// Libraries // Libraries
import _ from 'lodash'; import _ from 'lodash';
import Remarkable from 'remarkable';
// Services & utils // Services & utils
import coreModule from 'app/core/core_module'; import coreModule from 'app/core/core_module';
import config from 'app/core/config';
import { Emitter } from 'app/core/utils/emitter'; import { Emitter } from 'app/core/utils/emitter';
// Types // Types
import { DashboardModel } from '../dashboard/dashboard_model'; import { DashboardModel } from '../dashboard/dashboard_model';
import { PanelModel } from '../dashboard/panel_model';
import { DataQuery } from 'app/types';
export class MetricsTabCtrl { export interface AngularQueryComponentScope {
dsName: string; panel: PanelModel;
panel: any;
panelCtrl: any;
datasources: any[];
datasourceInstance: any;
nextRefId: string;
dashboard: DashboardModel; dashboard: DashboardModel;
panelDsValue: any;
addQueryDropdown: any;
queryTroubleshooterOpen: boolean;
helpOpen: boolean;
optionsOpen: boolean;
hasQueryHelp: boolean;
helpHtml: string;
queryOptions: any;
events: Emitter; events: Emitter;
refresh: () => void;
/** @ngInject */ render: () => void;
constructor($scope, private $sce, datasourceSrv, private backendSrv) { removeQuery: (query: DataQuery) => void;
this.panelCtrl = $scope.ctrl; addQuery: (query?: DataQuery) => void;
$scope.ctrl = this; moveQuery: (query: DataQuery, direction: number) => void;
this.panel = this.panelCtrl.panel;
this.panel.datasource = this.panel.datasource || null;
this.panel.targets = this.panel.targets || [{}];
this.dashboard = this.panelCtrl.dashboard;
this.datasources = datasourceSrv.getMetricSources();
this.panelDsValue = this.panelCtrl.panel.datasource;
// added here as old query controller expects this on panelCtrl but
// they are getting MetricsTabCtrl instead
this.events = this.panel.events;
for (const ds of this.datasources) {
if (ds.value === this.panelDsValue) {
this.datasourceInstance = ds;
}
}
this.addQueryDropdown = { text: 'Add Query', value: null, fake: true };
// update next ref id
this.nextRefId = this.dashboard.getNextQueryLetter(this.panel);
this.updateDatasourceOptions();
}
updateDatasourceOptions() {
if (this.datasourceInstance) {
this.hasQueryHelp = this.datasourceInstance.meta.hasQueryHelp;
this.queryOptions = this.datasourceInstance.meta.queryOptions;
}
}
getOptions(includeBuiltin) {
return Promise.resolve(
this.datasources
.filter(value => {
return includeBuiltin || !value.meta.builtIn;
})
.map(ds => {
return { value: ds.value, text: ds.name, datasource: ds };
})
);
}
datasourceChanged(option) {
if (!option) {
return;
}
this.setDatasource(option.datasource);
this.updateDatasourceOptions();
}
setDatasource(datasource) {
// switching to mixed
if (datasource.meta.mixed) {
_.each(this.panel.targets, target => {
target.datasource = this.panel.datasource;
if (!target.datasource) {
target.datasource = config.defaultDatasource;
}
});
} else if (this.datasourceInstance) {
// if switching from mixed
if (this.datasourceInstance.meta.mixed) {
_.each(this.panel.targets, target => {
delete target.datasource;
});
} else if (this.datasourceInstance.meta.id !== datasource.meta.id) {
// we are changing data source type, clear queries
this.panel.targets = [{ refId: 'A' }];
this.panelCtrl.nextRefId = this.dashboard.getNextQueryLetter(this.panel);
}
}
this.datasourceInstance = datasource;
this.panel.datasource = datasource.value;
this.panel.refresh();
}
addMixedQuery(option) {
if (!option) {
return;
}
this.panelCtrl.addQuery({
isNew: true,
datasource: option.datasource.name,
});
this.addQueryDropdown = { text: 'Add Query', value: null, fake: true };
}
toggleHelp() {
this.optionsOpen = false;
this.queryTroubleshooterOpen = false;
this.helpOpen = !this.helpOpen;
this.backendSrv.get(`/api/plugins/${this.datasourceInstance.meta.id}/markdown/query_help`).then(res => {
const md = new Remarkable();
this.helpHtml = this.$sce.trustAsHtml(md.render(res));
});
}
toggleOptions() {
this.helpOpen = false;
this.queryTroubleshooterOpen = false;
this.optionsOpen = !this.optionsOpen;
}
toggleQueryTroubleshooter() {
this.helpOpen = false;
this.optionsOpen = false;
this.queryTroubleshooterOpen = !this.queryTroubleshooterOpen;
}
addQuery(query?) {
query = query || {};
query.refId = this.dashboard.getNextQueryLetter(this.panel);
query.isNew = true;
this.panel.targets.push(query);
this.nextRefId = this.dashboard.getNextQueryLetter(this.panel);
}
refresh() {
this.panel.refresh();
}
render() {
this.panel.render();
}
removeQuery(target) {
const index = _.indexOf(this.panel.targets, target);
this.panel.targets.splice(index, 1);
this.nextRefId = this.dashboard.getNextQueryLetter(this.panel);
this.panel.refresh();
}
moveQuery(target, direction) {
const index = _.indexOf(this.panel.targets, target);
_.move(this.panel.targets, index, index + direction);
}
} }
/** @ngInject */ /** @ngInject */
@ -185,7 +28,6 @@ export function metricsTabDirective() {
restrict: 'E', restrict: 'E',
scope: true, scope: true,
templateUrl: 'public/app/features/panel/partials/metrics_tab.html', templateUrl: 'public/app/features/panel/partials/metrics_tab.html',
controller: MetricsTabCtrl,
}; };
} }

View File

@ -1,5 +1,3 @@
<div class="query-editor-rows gf-form-group" ng-if="ctrl.datasourceInstance">
<div ng-repeat="target in ctrl.panel.targets" ng-class="{'gf-form-disabled': target.hide}"> <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"> <rebuild-on-change property="ctrl.panel.datasource || target.datasource" show-null="true">
<plugin-component type="query-ctrl"> <plugin-component type="query-ctrl">
@ -7,21 +5,20 @@
</rebuild-on-change> </rebuild-on-change>
</div> </div>
<div class="gf-form-query"> <!-- <div class="gf&#45;form&#45;query"> -->
<div class="gf-form gf-form-query-letter-cell"> <!-- <div class="gf&#45;form gf&#45;form&#45;query&#45;letter&#45;cell"> -->
<label class="gf-form-label"> <!-- <label class="gf&#45;form&#45;label"> -->
<span class="gf-form-query-letter-cell-carret"> <!-- <span class="gf&#45;form&#45;query&#45;letter&#45;cell&#45;carret"> -->
<i class="fa fa-caret-down"></i> <!-- <i class="fa fa&#45;caret&#45;down"></i> -->
</span> <!-- </span> -->
<span class="gf-form-query-letter-cell-letter">{{ctrl.nextRefId}}</span> <!-- <span class="gf&#45;form&#45;query&#45;letter&#45;cell&#45;letter">{{ctrl.nextRefId}}</span> -->
</label> <!-- </label> -->
<button class="btn btn-secondary gf-form-btn" ng-click="ctrl.addQuery()" ng-hide="ctrl.datasourceInstance.meta.mixed"> <!-- <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 <!-- Add Query -->
</button> <!-- </button> -->
<div class="dropdown" ng-if="ctrl.datasourceInstance.meta.mixed"> <!-- <div class="dropdown" ng&#45;if="ctrl.datasourceInstance.meta.mixed"> -->
<gf-form-dropdown model="ctrl.addQueryDropdown" get-options="ctrl.getOptions(false)" on-change="ctrl.addMixedQuery($option)"> <!-- <gf&#45;form&#45;dropdown model="ctrl.addQueryDropdown" get&#45;options="ctrl.getOptions(false)" on&#45;change="ctrl.addMixedQuery($option)"> -->
</gf-form-dropdown> <!-- </gf&#45;form&#45;dropdown> -->
</div> <!-- </div> -->
</div> <!-- </div> -->
</div> <!-- </div> -->
</div>

View File

@ -20,7 +20,7 @@ export class QueryRowCtrl {
this.hideEditorRowActions = this.panelCtrl.hideEditorRowActions; this.hideEditorRowActions = this.panelCtrl.hideEditorRowActions;
if (!this.target.refId) { if (!this.target.refId) {
this.target.refId = this.panelCtrl.dashboard.getNextQueryLetter(this.panel); this.target.refId = this.panel.getNextQueryLetter();
} }
this.toggleCollapse(true); this.toggleCollapse(true);

View File

@ -110,7 +110,6 @@ $input-border: 1px solid $input-border-color;
&--grow { &--grow {
flex-grow: 1; flex-grow: 1;
min-height: 2.6rem;
} }
&--error { &--error {