Prometheus: Fixed Prometheus query editor error (plus new ErrorBoundaryAlert component) (#18838)

* ErrorHandling: Fixed Prometheus query editor error and added error boundary

* Update public/app/core/components/ErrorBoundary/ErrorBoundary.tsx

Co-Authored-By: kay delaney <45561153+kaydelaney@users.noreply.github.com>

* Fixed ts error
This commit is contained in:
Torkel Ödegaard 2019-09-02 20:45:18 +02:00 committed by GitHub
parent 29ff8e6018
commit fe658d7ac2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 56 additions and 17 deletions

View File

@ -1,15 +1,16 @@
import React, { FC } from 'react';
import React, { FC, ReactNode } from 'react';
interface Props {
message: any;
title: string;
button?: {
text: string;
onClick: (event: React.MouseEvent) => void;
};
children?: ReactNode;
}
export const Alert: FC<Props> = props => {
const { message, button } = props;
const { title, button, children } = props;
return (
<div className="alert-container">
<div className="alert-error alert">
@ -17,7 +18,8 @@ export const Alert: FC<Props> = props => {
<i className="fa fa-exclamation-triangle" />
</div>
<div className="alert-body">
<div className="alert-title">{message}</div>
<div className="alert-title">{title}</div>
{children && <div className="alert-text">{children}</div>}
</div>
{button && (
<div className="alert-button">

View File

@ -179,7 +179,7 @@ const LogRowContextGroup: React.FunctionComponent<LogRowContextGroupProps> = ({
}}
/>
)}
{error && <Alert message={error} />}
{error && <Alert title={error} />}
</div>
</CustomScrollbar>
</div>

View File

@ -1,4 +1,5 @@
import { Component } from 'react';
import React, { PureComponent, ReactNode } from 'react';
import { Alert } from '@grafana/ui';
interface ErrorInfo {
componentStack: string;
@ -10,7 +11,7 @@ interface RenderProps {
}
interface Props {
children: (r: RenderProps) => JSX.Element;
children: (r: RenderProps) => ReactNode;
}
interface State {
@ -18,7 +19,7 @@ interface State {
errorInfo: ErrorInfo;
}
class ErrorBoundary extends Component<Props, State> {
export class ErrorBoundary extends PureComponent<Props, State> {
readonly state: State = {
error: null,
errorInfo: null,
@ -41,4 +42,36 @@ class ErrorBoundary extends Component<Props, State> {
}
}
export default ErrorBoundary;
interface WithAlertBoxProps {
title?: string;
children: ReactNode;
}
export class ErrorBoundaryAlert extends PureComponent<WithAlertBoxProps> {
static defaultProps: Partial<WithAlertBoxProps> = {
title: 'An unexpected error happened',
};
render() {
const { title, children } = this.props;
return (
<ErrorBoundary>
{({ error, errorInfo }) => {
if (!errorInfo) {
return children;
}
return (
<Alert title={title}>
<details style={{ whiteSpace: 'pre-wrap' }}>
{error && error.toString()}
<br />
{errorInfo.componentStack}
</details>
</Alert>
);
}}
</ErrorBoundary>
);
}
}

View File

@ -5,7 +5,7 @@ import { Unsubscribable } from 'rxjs';
// Components
import { PanelHeader } from './PanelHeader/PanelHeader';
import ErrorBoundary from 'app/core/components/ErrorBoundary/ErrorBoundary';
import { ErrorBoundary } from 'app/core/components/ErrorBoundary/ErrorBoundary';
// Utils & Services
import { getTimeSrv, TimeSrv } from '../services/TimeSrv';

View File

@ -8,6 +8,7 @@ import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { AngularComponent, getAngularLoader } from '@grafana/runtime';
import { Emitter } from 'app/core/utils/emitter';
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { ErrorBoundaryAlert } from 'app/core/components/ErrorBoundary/ErrorBoundary';
// Types
import { PanelModel } from '../state/PanelModel';
@ -257,7 +258,9 @@ export class QueryEditorRow extends PureComponent<Props, State> {
</button>
</div>
</div>
<div className={bodyClasses}>{this.renderPluginEditor()}</div>
<div className={bodyClasses}>
<ErrorBoundaryAlert title="Data source query editor failed">{this.renderPluginEditor()}</ErrorBoundaryAlert>
</div>
</div>
);
}

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react';
export default class ErrorBoundary extends Component<{}, any> {
export class ErrorBoundary extends Component<{}, any> {
constructor(props: {}) {
super(props);
this.state = { error: null, errorInfo: null };

View File

@ -12,7 +12,7 @@ import store from 'app/core/store';
// Components
import { Alert } from '@grafana/ui';
import ErrorBoundary from './ErrorBoundary';
import { ErrorBoundary } from './ErrorBoundary';
import LogsContainer from './LogsContainer';
import QueryRows from './QueryRows';
import TableContainer from './TableContainer';
@ -263,7 +263,7 @@ export class Explore extends React.PureComponent<ExploreProps> {
<FadeIn duration={datasourceError ? 150 : 5} in={datasourceError ? true : false}>
<div className="explore-container">
<Alert
message={`Error connecting to datasource: ${datasourceError}`}
title={`Error connecting to datasource: ${datasourceError}`}
button={{ text: 'Reconnect', onClick: this.onReconnect }}
/>
</div>

View File

@ -5,7 +5,7 @@ import { connect } from 'react-redux';
import { StoreState } from 'app/types';
import { ExploreId } from 'app/types/explore';
import ErrorBoundary from './ErrorBoundary';
import { ErrorBoundary } from './ErrorBoundary';
import Explore from './Explore';
import { CustomScrollbar } from '@grafana/ui';
import { resetExploreAction } from './state/actionTypes';

View File

@ -154,7 +154,8 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
componentDidUpdate(prevProps: PromQueryFieldProps) {
const { queryResponse } = this.props;
if (prevProps.queryResponse && prevProps.queryResponse.series !== queryResponse.series) {
if (queryResponse && prevProps.queryResponse && prevProps.queryResponse.series !== queryResponse.series) {
this.refreshHint();
}
@ -177,7 +178,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
refreshHint = () => {
const { datasource, query, queryResponse } = this.props;
if (!queryResponse.series || queryResponse.series.length === 0) {
if (!queryResponse || queryResponse.series.length === 0) {
this.setState({ hint: null });
return;
}