mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Changed how react panels store their options * Added a ReactPanelPlugin as the interface that react panels export, this way react panels have clearer api, and gives us hooks to handle migrations and a way for panel to handle panel changes in the future * Moved gauge value options into a sub oject and made editor more generic, will be moved out of gauge pane later and shared between singlestat, gauge, bargauge, honecomb * Also remove nested options prop that was there due to bug * Added missing Gauge props * Fixed gauge issue that will require migration later and also value options editor did not handle null decimals or 0 decimals * Fixed unit tests * More fixes for react panels
219 lines
4.8 KiB
TypeScript
219 lines
4.8 KiB
TypeScript
// Library
|
|
import React, { Component } from 'react';
|
|
|
|
// Services
|
|
import { DatasourceSrv, getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
|
// Utils
|
|
import kbn from 'app/core/utils/kbn';
|
|
// Types
|
|
import {
|
|
DataQueryOptions,
|
|
DataQueryResponse,
|
|
DataQueryError,
|
|
LoadingState,
|
|
PanelData,
|
|
TableData,
|
|
TimeRange,
|
|
TimeSeries,
|
|
} from '@grafana/ui';
|
|
|
|
interface RenderProps {
|
|
loading: LoadingState;
|
|
panelData: PanelData;
|
|
}
|
|
|
|
export interface Props {
|
|
datasource: string | null;
|
|
queries: any[];
|
|
panelId: number;
|
|
dashboardId?: number;
|
|
isVisible?: boolean;
|
|
timeRange?: TimeRange;
|
|
widthPixels: number;
|
|
refreshCounter: number;
|
|
minInterval?: string;
|
|
maxDataPoints?: number;
|
|
children: (r: RenderProps) => JSX.Element;
|
|
onDataResponse?: (data: DataQueryResponse) => void;
|
|
onError: (message: string, error: DataQueryError) => void;
|
|
}
|
|
|
|
export interface State {
|
|
isFirstLoad: boolean;
|
|
loading: LoadingState;
|
|
response: DataQueryResponse;
|
|
}
|
|
|
|
export class DataPanel extends Component<Props, State> {
|
|
static defaultProps = {
|
|
isVisible: true,
|
|
dashboardId: 1,
|
|
};
|
|
|
|
dataSourceSrv: DatasourceSrv = getDatasourceSrv();
|
|
isUnmounted = false;
|
|
|
|
constructor(props: Props) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
loading: LoadingState.NotStarted,
|
|
response: {
|
|
data: [],
|
|
},
|
|
isFirstLoad: true,
|
|
};
|
|
}
|
|
|
|
componentDidMount() {
|
|
this.issueQueries();
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
this.isUnmounted = true;
|
|
}
|
|
|
|
async componentDidUpdate(prevProps: Props) {
|
|
if (!this.hasPropsChanged(prevProps)) {
|
|
return;
|
|
}
|
|
|
|
this.issueQueries();
|
|
}
|
|
|
|
hasPropsChanged(prevProps: Props) {
|
|
return this.props.refreshCounter !== prevProps.refreshCounter;
|
|
}
|
|
|
|
private issueQueries = async () => {
|
|
const {
|
|
isVisible,
|
|
queries,
|
|
datasource,
|
|
panelId,
|
|
dashboardId,
|
|
timeRange,
|
|
widthPixels,
|
|
maxDataPoints,
|
|
onDataResponse,
|
|
onError,
|
|
} = this.props;
|
|
|
|
if (!isVisible) {
|
|
return;
|
|
}
|
|
|
|
if (!queries.length) {
|
|
this.setState({ loading: LoadingState.Done });
|
|
return;
|
|
}
|
|
|
|
this.setState({ loading: LoadingState.Loading });
|
|
|
|
try {
|
|
const ds = await this.dataSourceSrv.get(datasource);
|
|
|
|
// TODO interpolate variables
|
|
const minInterval = this.props.minInterval || ds.interval;
|
|
const intervalRes = kbn.calculateInterval(timeRange, widthPixels, minInterval);
|
|
|
|
const queryOptions: DataQueryOptions = {
|
|
timezone: 'browser',
|
|
panelId: panelId,
|
|
dashboardId: dashboardId,
|
|
range: timeRange,
|
|
rangeRaw: timeRange.raw,
|
|
interval: intervalRes.interval,
|
|
intervalMs: intervalRes.intervalMs,
|
|
targets: queries,
|
|
maxDataPoints: maxDataPoints || widthPixels,
|
|
scopedVars: {},
|
|
cacheTimeout: null,
|
|
};
|
|
|
|
const resp = await ds.query(queryOptions);
|
|
|
|
if (this.isUnmounted) {
|
|
return;
|
|
}
|
|
|
|
if (onDataResponse) {
|
|
onDataResponse(resp);
|
|
}
|
|
|
|
this.setState({
|
|
loading: LoadingState.Done,
|
|
response: resp,
|
|
isFirstLoad: false,
|
|
});
|
|
} catch (err) {
|
|
console.log('DataPanel error', err);
|
|
|
|
let message = 'Query error';
|
|
|
|
if (err.message) {
|
|
message = err.message;
|
|
} else if (err.data && err.data.message) {
|
|
message = err.data.message;
|
|
} else if (err.data && err.data.error) {
|
|
message = err.data.error;
|
|
} else if (err.status) {
|
|
message = `Query error: ${err.status} ${err.statusText}`;
|
|
}
|
|
|
|
onError(message, err);
|
|
this.setState({ isFirstLoad: false, loading: LoadingState.Error });
|
|
}
|
|
};
|
|
|
|
getPanelData = () => {
|
|
const { response } = this.state;
|
|
|
|
if (response.data.length > 0 && (response.data[0] as TableData).type === 'table') {
|
|
return {
|
|
tableData: response.data[0] as TableData,
|
|
timeSeries: null,
|
|
};
|
|
}
|
|
|
|
return {
|
|
timeSeries: response.data as TimeSeries[],
|
|
tableData: null,
|
|
};
|
|
};
|
|
|
|
render() {
|
|
const { queries } = this.props;
|
|
const { loading, isFirstLoad } = this.state;
|
|
const panelData = this.getPanelData();
|
|
|
|
// do not render component until we have first data
|
|
if (isFirstLoad && (loading === LoadingState.Loading || loading === LoadingState.NotStarted)) {
|
|
return this.renderLoadingState();
|
|
}
|
|
|
|
if (!queries.length) {
|
|
return (
|
|
<div className="panel-empty">
|
|
<p>Add a query to get some data!</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{loading === LoadingState.Loading && this.renderLoadingState()}
|
|
{this.props.children({ loading, panelData })}
|
|
</>
|
|
);
|
|
}
|
|
|
|
private renderLoadingState(): JSX.Element {
|
|
return (
|
|
<div className="panel-loading">
|
|
<i className="fa fa-spinner fa-spin" />
|
|
</div>
|
|
);
|
|
}
|
|
}
|