use render props pattern

pull/14751/head
Erik Sundell 7 years ago
parent 07dc9d06d7
commit 9b1b4c09f5
  1. 40
      public/app/plugins/datasource/stackdriver/components/Aggregations.tsx
  2. 41
      public/app/plugins/datasource/stackdriver/components/Filter.tsx
  3. 20
      public/app/plugins/datasource/stackdriver/components/Metrics.tsx
  4. 170
      public/app/plugins/datasource/stackdriver/components/QueryEditor.tsx

@ -11,8 +11,10 @@ import { StackdriverPicker } from './StackdriverPicker';
export interface Props { export interface Props {
onChange: (metricDescriptor) => void; onChange: (metricDescriptor) => void;
templateSrv: any; templateSrv: any;
valueType: string; metricDescriptor: {
metricKind: string; valueType: string;
metricKind: string;
};
aggregation: { aggregation: {
crossSeriesReducer: string; crossSeriesReducer: string;
alignmentPeriod: string; alignmentPeriod: string;
@ -39,26 +41,31 @@ export class Aggregations extends React.Component<Props, State> {
} }
componentDidMount() { componentDidMount() {
this.setAggOptions(this.props); if (this.props.metricDescriptor !== null) {
this.setAggOptions(this.props);
}
} }
componentWillReceiveProps(nextProps: Props) { componentWillReceiveProps(nextProps: Props) {
const { valueType, metricKind, aggregation } = this.props; // const { metricDescriptor, aggregation } = this.props;
if ( // if (
nextProps.valueType !== valueType || // (metricDescriptor !== null && nextProps.metricDescriptor.valueType !== metricDescriptor.valueType) ||
nextProps.metricKind !== metricKind || // nextProps.metricDescriptor.metricKind !== metricDescriptor.metricKind ||
nextProps.aggregation.groupBys !== aggregation.groupBys // nextProps.aggregation.groupBys !== aggregation.groupBys
) { // ) {
if (nextProps.metricDescriptor !== null) {
this.setAggOptions(nextProps); this.setAggOptions(nextProps);
} }
} }
setAggOptions({ valueType, metricKind, aggregation }) { setAggOptions({ metricDescriptor, aggregation }) {
const { templateSrv } = this.props; const { templateSrv } = this.props;
let aggregations = getAggregationOptionsByMetric(valueType, metricKind).map(a => ({ let aggregations = getAggregationOptionsByMetric(metricDescriptor.valueType, metricDescriptor.metricKind).map(
...a, a => ({
label: a.text, ...a,
})); label: a.text,
})
);
if ( if (
aggregations.length > 0 && aggregations.length > 0 &&
@ -75,7 +82,10 @@ export class Aggregations extends React.Component<Props, State> {
} }
deselectAggregationOption(notValidOptionValue: string) { deselectAggregationOption(notValidOptionValue: string) {
const aggregations = getAggregationOptionsByMetric(this.props.valueType, this.props.metricKind); const aggregations = getAggregationOptionsByMetric(
this.props.metricDescriptor.valueType,
this.props.metricDescriptor.metricKind
);
const newValue = aggregations.find(o => o.value !== notValidOptionValue); const newValue = aggregations.find(o => o.value !== notValidOptionValue);
this.handleAggregationChange(newValue ? newValue.value : ''); this.handleAggregationChange(newValue ? newValue.value : '');
} }

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import _ from 'lodash'; import _ from 'lodash';
import appEvents from 'app/core/app_events';
import { QueryMeta, Target } from '../types'; import { QueryMeta, Target } from '../types';
import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader'; import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
@ -8,14 +9,19 @@ import '../query_filter_ctrl';
export interface Props { export interface Props {
filtersChanged: (filters) => void; filtersChanged: (filters) => void;
groupBysChanged: (groupBys) => void; groupBysChanged: (groupBys) => void;
metricType: string;
templateSrv: any; templateSrv: any;
labelData: QueryMeta;
loading: Promise<any>;
target: Target; target: Target;
uiSegmentSrv: any; uiSegmentSrv: any;
datasource: any;
} }
export class Filter extends React.Component<Props, any> { interface State {
labelData: QueryMeta;
loading: Promise<any>;
}
export class Filter extends React.Component<Props, State> {
element: any; element: any;
component: AngularComponent; component: AngularComponent;
@ -24,13 +30,13 @@ export class Filter extends React.Component<Props, any> {
return; return;
} }
const { loading, labelData, target, filtersChanged, groupBysChanged } = this.props; const { target, filtersChanged, groupBysChanged } = this.props;
const loader = getAngularLoader(); const loader = getAngularLoader();
const template = '<stackdriver-filter> </stackdriver-filter>'; const template = '<stackdriver-filter> </stackdriver-filter>';
const scopeProps = { const scopeProps = {
loading, loading: this.loadLabels.bind(this),
labelData, labelData: null,
target, target,
filtersChanged, filtersChanged,
groupBysChanged, groupBysChanged,
@ -39,11 +45,11 @@ export class Filter extends React.Component<Props, any> {
this.component = loader.load(this.element, scopeProps, template); this.component = loader.load(this.element, scopeProps, template);
} }
componentDidUpdate() { componentDidUpdate(prevProps: Props) {
const scope = this.component.getScope(); if (prevProps.metricType !== this.props.metricType) {
scope.loading = _.clone(this.props.loading); const scope = this.component.getScope();
scope.labelData = _.cloneDeep(this.props.labelData); scope.loading = this.loadLabels(scope);
scope.target = _.cloneDeep(this.props.target); }
} }
componentWillUnmount() { componentWillUnmount() {
@ -52,6 +58,19 @@ export class Filter extends React.Component<Props, any> {
} }
} }
async loadLabels(scope) {
return new Promise(async resolve => {
try {
const { meta } = await this.props.datasource.getLabels(this.props.target.metricType, this.props.target.refId);
scope.labelData = meta;
resolve();
} catch (error) {
appEvents.emit('alert-error', ['Error', 'Error loading metric labels for ' + this.props.target.metricType]);
resolve();
}
});
}
render() { render() {
return <div ref={element => (this.element = element)} style={{ width: '100%' }} />; return <div ref={element => (this.element = element)} style={{ width: '100%' }} />;
} }

@ -9,6 +9,7 @@ export interface Props {
datasource: any; datasource: any;
defaultProject: string; defaultProject: string;
metricType: string; metricType: string;
children?: (renderProps: any) => JSX.Element;
} }
interface State { interface State {
@ -17,6 +18,7 @@ interface State {
services: any[]; services: any[];
service: string; service: string;
metric: string; metric: string;
metricDescriptor: any;
} }
export class Metrics extends React.Component<Props, State> { export class Metrics extends React.Component<Props, State> {
@ -26,6 +28,7 @@ export class Metrics extends React.Component<Props, State> {
services: [], services: [],
service: '', service: '',
metric: '', metric: '',
metricDescriptor: null,
}; };
constructor(props) { constructor(props) {
@ -68,13 +71,16 @@ export class Metrics extends React.Component<Props, State> {
const services = this.getServicesList(metricDescriptors); const services = this.getServicesList(metricDescriptors);
const metrics = this.getMetricsList(metricDescriptors); const metrics = this.getMetricsList(metricDescriptors);
const service = metrics.length > 0 ? metrics[0].service : ''; const service = metrics.length > 0 ? metrics[0].service : '';
this.setState({ metricDescriptors, services, metrics, service: service }); const metricDescriptor = this.getSelectedMetricDescriptor(this.props.metricType);
this.setState({ metricDescriptors, services, metrics, service: service, metricDescriptor });
}
getSelectedMetricDescriptor(metricType) {
return this.state.metricDescriptors.find(md => md.type === this.props.templateSrv.replace(metricType));
} }
getMetricsList(metricDescriptors) { getMetricsList(metricDescriptors) {
const selectedMetricDescriptor = metricDescriptors.find( const selectedMetricDescriptor = this.getSelectedMetricDescriptor(this.props.metricType);
md => md.type === this.props.templateSrv.replace(this.props.metricType)
);
const metricsByService = metricDescriptors.filter(m => m.service === selectedMetricDescriptor.service).map(m => ({ const metricsByService = metricDescriptors.filter(m => m.service === selectedMetricDescriptor.service).map(m => ({
service: m.service, service: m.service,
value: m.type, value: m.type,
@ -103,8 +109,9 @@ export class Metrics extends React.Component<Props, State> {
} }
handleMetricTypeChange(value) { handleMetricTypeChange(value) {
const selectedMetricDescriptor = this.state.metricDescriptors.find(md => md.type === value); const metricDescriptor = this.getSelectedMetricDescriptor(value);
this.props.onChange(selectedMetricDescriptor); this.setState({ metricDescriptor });
this.props.onChange(metricDescriptor);
} }
getServicesList(metricDescriptors) { getServicesList(metricDescriptors) {
@ -166,6 +173,7 @@ export class Metrics extends React.Component<Props, State> {
<div className="gf-form-label gf-form-label--grow" /> <div className="gf-form-label gf-form-label--grow" />
</div> </div>
</div> </div>
{this.props.children(this.state.metricDescriptor)}
</React.Fragment> </React.Fragment>
); );
} }

@ -1,11 +1,10 @@
import React from 'react'; import React from 'react';
import _ from 'lodash'; import _ from 'lodash';
import appEvents from 'app/core/app_events';
import { Metrics } from './Metrics'; import { Metrics } from './Metrics';
import { Filter } from './Filter'; import { Filter } from './Filter';
import { Aggregations } from './Aggregations'; import { Aggregations } from './Aggregations';
import { Target, QueryMeta } from '../types'; import { Target } from '../types';
export interface Props { export interface Props {
onQueryChange: (target: Target) => void; onQueryChange: (target: Target) => void;
@ -18,8 +17,6 @@ export interface Props {
interface State { interface State {
target: Target; target: Target;
labelData: QueryMeta;
loadLabelsPromise: Promise<any>;
} }
const DefaultTarget: Target = { const DefaultTarget: Target = {
@ -41,85 +38,79 @@ const DefaultTarget: Target = {
}; };
export class QueryEditor extends React.Component<Props, State> { export class QueryEditor extends React.Component<Props, State> {
state: State = { labelData: null, loadLabelsPromise: new Promise(() => {}), target: DefaultTarget }; state: State = { target: DefaultTarget };
componentDidMount() { componentDidMount() {
this.getLabels();
this.setState({ target: this.props.target }); this.setState({ target: this.props.target });
} }
async getLabels() {
const loadLabelsPromise = new Promise(async resolve => {
try {
const { meta } = await this.props.datasource.getLabels(this.props.target.metricType, this.props.target.refId);
this.setState({ labelData: meta });
resolve();
} catch (error) {
appEvents.emit('alert-error', ['Error', 'Error loading metric labels for ' + this.props.target.metricType]);
resolve();
}
});
this.setState({ loadLabelsPromise });
}
handleMetricTypeChange({ valueType, metricKind, type, unit }) { handleMetricTypeChange({ valueType, metricKind, type, unit }) {
this.setState({ this.setState(
target: { {
...this.state.target, target: {
...{ ...this.state.target,
metricType: type, ...{
unit, metricType: type,
valueType, unit,
metricKind, valueType,
metricKind,
},
}, },
}, },
}); () => {
this.props.onQueryChange(this.state.target);
// this.$rootScope.$broadcast('metricTypeChanged'); this.props.onExecuteQuery();
this.getLabels(); }
this.props.onQueryChange(this.state.target); );
this.props.onExecuteQuery();
} }
handleFilterChange(value) { handleFilterChange(value) {
this.setState({ this.setState(
target: { {
...this.state.target, target: {
filters: value, ...this.state.target,
filters: value,
},
}, },
}); () => {
this.props.onQueryChange(this.state.target); this.props.onQueryChange(this.state.target);
this.props.onExecuteQuery(); this.props.onExecuteQuery();
}
);
} }
handleGroupBysChange(value) { handleGroupBysChange(value) {
this.setState({ this.setState(
target: { {
...this.state.target, target: {
groupBys: value, ...this.state.target,
groupBys: value,
},
}, },
}); () => {
this.props.onQueryChange(this.state.target); this.props.onQueryChange(this.state.target);
this.props.onExecuteQuery(); this.props.onExecuteQuery();
}
);
} }
handleAggregationChange(value) { handleAggregationChange(value) {
this.setState({ const target = {
target: { ...this.state.target,
...this.state.target, aggregation: {
aggregation: { ...this.state.target.aggregation,
...this.state.target.aggregation, crossSeriesReducer: value,
crossSeriesReducer: value,
},
}, },
};
this.setState({ target }, () => {
this.props.onQueryChange(target);
this.props.onExecuteQuery();
}); });
this.props.onQueryChange(this.state.target);
this.props.onExecuteQuery();
} }
render() { render() {
const { labelData, loadLabelsPromise, target } = this.state; const { target } = this.state;
const { defaultProject, metricType, valueType, metricKind, aggregation } = target; const { defaultProject, metricType, aggregation } = target;
const { templateSrv, datasource, uiSegmentSrv } = this.props; const { templateSrv, datasource, uiSegmentSrv } = this.props;
return ( return (
@ -130,46 +121,27 @@ export class QueryEditor extends React.Component<Props, State> {
templateSrv={templateSrv} templateSrv={templateSrv}
datasource={datasource} datasource={datasource}
onChange={value => this.handleMetricTypeChange(value)} onChange={value => this.handleMetricTypeChange(value)}
/> >
<Filter {metric => (
filtersChanged={value => this.handleFilterChange(value)} <React.Fragment>
groupBysChanged={value => this.handleGroupBysChange(value)} <Filter
target={target} filtersChanged={value => this.handleFilterChange(value)}
uiSegmentSrv={uiSegmentSrv} groupBysChanged={value => this.handleGroupBysChange(value)}
labelData={labelData} target={target}
templateSrv={templateSrv} uiSegmentSrv={uiSegmentSrv}
loading={loadLabelsPromise} templateSrv={templateSrv}
/> datasource={datasource}
<Aggregations metricType={metric ? metric.type : ''}
valueType={valueType} />
metricKind={metricKind} <Aggregations
templateSrv={templateSrv} metricDescriptor={metric}
aggregation={aggregation} templateSrv={templateSrv}
onChange={value => this.handleAggregationChange(value)} aggregation={aggregation}
/> onChange={value => this.handleAggregationChange(value)}
{/* target="ctrl.target" refresh="ctrl.refresh()" loading="ctrl.loadLabelsPromise" label-data="ctrl.labelData" */} />
{/* <stackdriver-filter </React.Fragment>
target="target" )}
refresh="target.refresh()" </Metrics>
loading="target.loadLabelsPromise"
label-data="target.labelData"
/>
<aggregation-picker
value-type="target.target.valueType"
metric-kind="target.target.metricKind"
aggregation="target.target.aggregation"
alignment-period="target.lastQueryMeta.alignmentPeriod"
refresh="target.refresh()"
template-srv="target.templateSrv"
datasource="target.datasource"
on-change="target.handleAggregationChange"
/>
<stackdriver-aggregation
target="target.target"
alignment-period="target.lastQueryMeta.alignmentPeriod"
refresh="target.refresh()"
/> */}
</React.Fragment> </React.Fragment>
); );
} }

Loading…
Cancel
Save