ErrorHandling: Error boundary for every container (#18845)

* ErrorHandling: Error boundary for every container

* Remvoe custom query editor errors
This commit is contained in:
Torkel Ödegaard 2019-09-04 13:59:30 +02:00 committed by GitHub
parent b0237c6b7e
commit 490a1d6fc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 57 additions and 64 deletions

View File

@ -1,13 +1,14 @@
import React, { PureComponent, ReactNode } from 'react';
import { Alert } from '@grafana/ui';
import { Alert } from '../Alert/Alert';
import { css } from 'emotion';
interface ErrorInfo {
componentStack: string;
}
interface RenderProps {
error: Error;
errorInfo: ErrorInfo;
error: Error | null;
errorInfo: ErrorInfo | null;
}
interface Props {
@ -15,8 +16,8 @@ interface Props {
}
interface State {
error: Error;
errorInfo: ErrorInfo;
error: Error | null;
errorInfo: ErrorInfo | null;
}
export class ErrorBoundary extends PureComponent<Props, State> {
@ -35,6 +36,7 @@ export class ErrorBoundary extends PureComponent<Props, State> {
render() {
const { children } = this.props;
const { error, errorInfo } = this.state;
return children({
error,
errorInfo,
@ -42,18 +44,28 @@ export class ErrorBoundary extends PureComponent<Props, State> {
}
}
function getAlertPageStyle() {
return css`
width: 500px;
margin: 64px auto;
`;
}
interface WithAlertBoxProps {
title?: string;
children: ReactNode;
style?: 'page' | 'alertbox';
}
export class ErrorBoundaryAlert extends PureComponent<WithAlertBoxProps> {
static defaultProps: Partial<WithAlertBoxProps> = {
title: 'An unexpected error happened',
style: 'alertbox',
};
render() {
const { title, children } = this.props;
const { title, children, style } = this.props;
return (
<ErrorBoundary>
{({ error, errorInfo }) => {
@ -61,15 +73,28 @@ export class ErrorBoundaryAlert extends PureComponent<WithAlertBoxProps> {
return children;
}
return (
<Alert title={title}>
<details style={{ whiteSpace: 'pre-wrap' }}>
{error && error.toString()}
<br />
{errorInfo.componentStack}
</details>
</Alert>
);
if (style === 'alertbox') {
return (
<Alert title={title || ''}>
<details style={{ whiteSpace: 'pre-wrap' }}>
{error && error.toString()}
<br />
{errorInfo.componentStack}
</details>
</Alert>
);
} else {
return (
<div className={getAlertPageStyle()}>
<h2>{title}</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
{error && error.toString()}
<br />
{errorInfo.componentStack}
</details>
</div>
);
}
}}
</ErrorBoundary>
);

View File

@ -78,3 +78,5 @@ export { VariableSuggestion, VariableOrigin } from './DataLinks/DataLinkSuggesti
export { DataLinksEditor } from './DataLinks/DataLinksEditor';
export { DataLinksContextMenu } from './DataLinks/DataLinksContextMenu';
export { SeriesIcon } from './Legend/SeriesIcon';
export { ErrorBoundary, ErrorBoundaryAlert } from './ErrorBoundary/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 '@grafana/ui';
// Utils & Services
import { getTimeSrv, TimeSrv } from '../services/TimeSrv';

View File

@ -8,11 +8,10 @@ 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';
import { DataQuery, DataSourceApi, PanelData, DataQueryRequest } from '@grafana/ui';
import { DataQuery, DataSourceApi, PanelData, DataQueryRequest, ErrorBoundaryAlert } from '@grafana/ui';
import { TimeRange, LoadingState } from '@grafana/data';
import { DashboardModel } from '../state/DashboardModel';
@ -259,7 +258,7 @@ export class QueryEditorRow extends PureComponent<Props, State> {
</div>
</div>
<div className={bodyClasses}>
<ErrorBoundaryAlert title="Data source query editor failed">{this.renderPluginEditor()}</ErrorBoundaryAlert>
<ErrorBoundaryAlert>{this.renderPluginEditor()}</ErrorBoundaryAlert>
</div>
</div>
);

View File

@ -1,34 +0,0 @@
import React, { Component } from 'react';
export class ErrorBoundary extends Component<{}, any> {
constructor(props: {}) {
super(props);
this.state = { error: null, errorInfo: null };
}
componentDidCatch(error: any, errorInfo: any) {
// Catch errors in any components below and re-render with error message
this.setState({
error,
errorInfo,
});
}
render() {
if (this.state.errorInfo) {
// Error path
return (
<div className="explore-container">
<h3>An unexpected error happened.</h3>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.error && this.state.error.toString()}
<br />
{this.state.errorInfo.componentStack}
</details>
</div>
);
}
// Normally, just render children
return this.props.children;
}
}

View File

@ -9,8 +9,7 @@ import memoizeOne from 'memoize-one';
// Services & Utils
import store from 'app/core/store';
// Components
import { Alert, DataQuery, ExploreStartPageProps, DataSourceApi, PanelData } from '@grafana/ui';
import { ErrorBoundary } from './ErrorBoundary';
import { Alert, ErrorBoundaryAlert, DataQuery, ExploreStartPageProps, DataSourceApi, PanelData } from '@grafana/ui';
import LogsContainer from './LogsContainer';
import QueryRows from './QueryRows';
import TableContainer from './TableContainer';
@ -275,7 +274,7 @@ export class Explore extends React.PureComponent<ExploreProps> {
return (
<main className="m-t-2" style={{ width }}>
<ErrorBoundary>
<ErrorBoundaryAlert>
{showingStartPage && <StartPage onClickExample={this.onClickExample} />}
{!showingStartPage && (
<>
@ -310,7 +309,7 @@ export class Explore extends React.PureComponent<ExploreProps> {
)}
</>
)}
</ErrorBoundary>
</ErrorBoundaryAlert>
</main>
);
}}

View File

@ -5,9 +5,8 @@ import { connect } from 'react-redux';
import { StoreState } from 'app/types';
import { ExploreId } from 'app/types/explore';
import { ErrorBoundary } from './ErrorBoundary';
import Explore from './Explore';
import { CustomScrollbar } from '@grafana/ui';
import { CustomScrollbar, ErrorBoundaryAlert } from '@grafana/ui';
import { resetExploreAction } from './state/actionTypes';
interface WrapperProps {
@ -27,13 +26,13 @@ export class Wrapper extends Component<WrapperProps> {
<div className="page-scrollbar-wrapper">
<CustomScrollbar autoHeightMin={'100%'} autoHeightMax={''} className="custom-scrollbar--page">
<div className="explore-wrapper">
<ErrorBoundary>
<ErrorBoundaryAlert style="page">
<Explore exploreId={ExploreId.left} />
</ErrorBoundary>
</ErrorBoundaryAlert>
{split && (
<ErrorBoundary>
<ErrorBoundaryAlert style="page">
<Explore exploreId={ExploreId.right} />
</ErrorBoundary>
</ErrorBoundaryAlert>
)}
</div>
</CustomScrollbar>

View File

@ -8,11 +8,14 @@ import coreModule from 'app/core/core_module';
import { store } from 'app/store/store';
import { ContextSrv } from 'app/core/services/context_srv';
import { provideTheme } from 'app/core/utils/ConfigProvider';
import { ErrorBoundaryAlert } from '@grafana/ui';
function WrapInProvider(store: any, Component: any, props: any) {
return (
<Provider store={store}>
<Component {...props} />
<ErrorBoundaryAlert style="page">
<Component {...props} />
</ErrorBoundaryAlert>
</Provider>
);
}