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 => {
_.each(panel.targets, target => {
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();
}
getNextQueryLetter(panel) {
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
return _.find(letters, refId => {
return _.every(panel.targets, other => {
return other.refId !== refId;
});
});
}
isTimezoneUtc() {
return this.getTimezone() === 'utc';
}

View File

@ -1,21 +1,27 @@
// Libraries
import React, { SFC, PureComponent } from 'react';
import Remarkable from 'remarkable';
import _ from 'lodash';
// Components
import DataSourceOption from './DataSourceOption';
import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
import { EditorTabBody } from './EditorTabBody';
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 { TimeRangeOptions } from './TimeRangeOptions';
import './../../panel/metrics_tab';
import { AngularQueryComponentScope } from 'app/features/panel/metrics_tab';
// Services
import { getDatasourceSrv } from 'app/features/plugins/datasource_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 {
panel: PanelModel;
@ -40,7 +46,7 @@ interface LoadingPlaceholderProps {
const LoadingPlaceholder: SFC<LoadingPlaceholderProps> = ({ text }) => <h2>{text}</h2>;
export class QueriesTab extends PureComponent<Props, State> {
element: any;
element: HTMLElement;
component: AngularComponent;
datasources: DataSourceSelectItem[] = getDatasourceSrv().getMetricSources();
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() {
if (!this.element) {
return;
}
const { panel, dashboard } = this.props;
const loader = getAngularLoader();
const template = '<metrics-tab />';
const scopeProps = {
ctrl: {
panel: panel,
dashboard: dashboard,
refresh: () => panel.refresh(),
},
ctrl: this.getAngularQueryComponentScope(),
};
this.component = loader.load(this.element, scopeProps, template);
@ -87,6 +103,7 @@ export class QueriesTab extends PureComponent<Props, State> {
onChangeDataSource = datasource => {
const { panel } = this.props;
const { currentDatasource } = this.state;
// switching to mixed
if (datasource.meta.mixed) {
panel.targets.forEach(target => {
@ -95,10 +112,16 @@ export class QueriesTab extends PureComponent<Props, State> {
target.datasource = config.defaultDatasource;
}
});
} else if (currentDatasource && currentDatasource.meta.mixed) {
panel.targets.forEach(target => {
delete target.datasource;
});
} else if (currentDatasource) {
// if switching from mixed
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;
@ -227,9 +250,41 @@ export class QueriesTab extends PureComponent<Props, State> {
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() {
const { panel } = this.props;
const { currentDatasource } = this.state;
const { hasQueryHelp } = currentDatasource.meta;
const dsInformation = {
title: currentDatasource.name,
imgSrc: currentDatasource.meta.info.logos.small,
@ -266,9 +321,23 @@ export class QueriesTab extends PureComponent<Props, State> {
return (
<EditorTabBody heading="Queries" main={dsInformation} toolbarItems={[options, queryInspector, dsHelp]}>
<>
<div ref={element => (this.element = element)} style={{ width: '100%' }} />
</>
<div className="query-editor-rows gf-form-group">
<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>
);
}

View File

@ -1,6 +1,7 @@
import { Emitter } from 'app/core/utils/emitter';
import _ from 'lodash';
import { PANEL_OPTIONS_KEY_PREFIX } from 'app/core/constants';
import { DataQuery } from 'app/types';
export interface GridPos {
x: number;
@ -237,6 +238,23 @@ export class PanelModel {
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() {
this.events.emit('panel-teardown');
this.events.removeAllListeners();

View File

@ -1,181 +1,24 @@
// Libraries
import _ from 'lodash';
import Remarkable from 'remarkable';
// Services & utils
import coreModule from 'app/core/core_module';
import config from 'app/core/config';
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 class MetricsTabCtrl {
dsName: string;
panel: any;
panelCtrl: any;
datasources: any[];
datasourceInstance: any;
nextRefId: string;
export interface AngularQueryComponentScope {
panel: PanelModel;
dashboard: DashboardModel;
panelDsValue: any;
addQueryDropdown: any;
queryTroubleshooterOpen: boolean;
helpOpen: boolean;
optionsOpen: boolean;
hasQueryHelp: boolean;
helpHtml: string;
queryOptions: any;
events: Emitter;
/** @ngInject */
constructor($scope, private $sce, datasourceSrv, private backendSrv) {
this.panelCtrl = $scope.ctrl;
$scope.ctrl = this;
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);
}
refresh: () => void;
render: () => void;
removeQuery: (query: DataQuery) => void;
addQuery: (query?: DataQuery) => void;
moveQuery: (query: DataQuery, direction: number) => void;
}
/** @ngInject */
@ -185,7 +28,6 @@ export function metricsTabDirective() {
restrict: 'E',
scope: true,
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}">
<rebuild-on-change property="ctrl.panel.datasource || target.datasource" show-null="true">
<plugin-component type="query-ctrl">
@ -7,21 +5,20 @@
</rebuild-on-change>
</div>
<div class="gf-form-query">
<div class="gf-form gf-form-query-letter-cell">
<label class="gf-form-label">
<span class="gf-form-query-letter-cell-carret">
<i class="fa fa-caret-down"></i>
</span>
<span class="gf-form-query-letter-cell-letter">{{ctrl.nextRefId}}</span>
</label>
<button class="btn btn-secondary gf-form-btn" ng-click="ctrl.addQuery()" ng-hide="ctrl.datasourceInstance.meta.mixed">
Add Query
</button>
<div class="dropdown" ng-if="ctrl.datasourceInstance.meta.mixed">
<gf-form-dropdown model="ctrl.addQueryDropdown" get-options="ctrl.getOptions(false)" on-change="ctrl.addMixedQuery($option)">
</gf-form-dropdown>
</div>
</div>
</div>
</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> -->

View File

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

View File

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