Alerting: wrap top level components in ErrorBoundary (#34040)

This commit is contained in:
Domas 2021-05-18 13:56:31 +03:00 committed by GitHub
parent 8ecfad6995
commit 098b4fc319
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 182 additions and 124 deletions

View File

@ -54,3 +54,21 @@ import { ErrorWithStack } from '@grafana/ui';
<ArgsTable of={ErrorWithStack}/>
# withErrorBoundary
a HOC to conveniently wrap component in an error boundary
### Usage
```jsx
import { withErrorBoundary } from '@grafana/ui';
interface MyCompProps {
...
}
const MyComp = withErrorBoundary((props: MyCompProps) => {
return <>...</>
}, { style: 'page' });
```

View File

@ -1,4 +1,4 @@
import React, { PureComponent, ReactNode } from 'react';
import React, { PureComponent, ReactNode, ComponentType } from 'react';
import { captureException } from '@sentry/browser';
import { Alert } from '../Alert/Alert';
import { ErrorWithStack } from './ErrorWithStack';
@ -46,14 +46,24 @@ export class ErrorBoundary extends PureComponent<Props, State> {
}
}
interface WithAlertBoxProps {
/**
* Props for the ErrorBoundaryAlert component
*
* @public
*/
export interface ErrorBoundaryAlertProps {
/** Title for the error boundary alert */
title?: string;
/** Component to be wrapped with an error boundary */
children: ReactNode;
/** 'page' will render full page error with stacktrace. 'alertbox' will render an <Alert />. Default 'alertbox' */
style?: 'page' | 'alertbox';
}
export class ErrorBoundaryAlert extends PureComponent<WithAlertBoxProps> {
static defaultProps: Partial<WithAlertBoxProps> = {
export class ErrorBoundaryAlert extends PureComponent<ErrorBoundaryAlertProps> {
static defaultProps: Partial<ErrorBoundaryAlertProps> = {
title: 'An unexpected error happened',
style: 'alertbox',
};
@ -86,3 +96,25 @@ export class ErrorBoundaryAlert extends PureComponent<WithAlertBoxProps> {
);
}
}
/**
* HOC for wrapping a component in an error boundary.
*
* @param Component - the react component to wrap in error boundary
* @param errorBoundaryProps - error boundary options
*
* @public
*/
export function withErrorBoundary<P = {}>(
Component: ComponentType<P>,
errorBoundaryProps: Omit<ErrorBoundaryAlertProps, 'children'> = {}
): ComponentType<P> {
const comp = (props: P) => (
<ErrorBoundaryAlert {...errorBoundaryProps}>
<Component {...props} />
</ErrorBoundaryAlert>
);
comp.displayName = 'WithErrorBoundary';
return comp;
}

View File

@ -132,7 +132,12 @@ export { FeatureBadge, FeatureInfoBox } from './InfoBox/FeatureInfoBox';
export { JSONFormatter } from './JSONFormatter/JSONFormatter';
export { JsonExplorer } from './JSONFormatter/json_explorer/json_explorer';
export { ErrorBoundary, ErrorBoundaryAlert } from './ErrorBoundary/ErrorBoundary';
export {
ErrorBoundary,
ErrorBoundaryAlert,
ErrorBoundaryAlertProps,
withErrorBoundary,
} from './ErrorBoundary/ErrorBoundary';
export { ErrorWithStack } from './ErrorBoundary/ErrorWithStack';
export { AlphaNotice } from './AlphaNotice/AlphaNotice';
export { DataSourceHttpSettings } from './DataSourceSettings/DataSourceHttpSettings';

View File

@ -1,7 +1,7 @@
import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { css } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data';
import { Alert, LoadingPlaceholder, useStyles2 } from '@grafana/ui';
import { Alert, LoadingPlaceholder, useStyles2, withErrorBoundary } from '@grafana/ui';
import { useDispatch } from 'react-redux';
import { Redirect } from 'react-router-dom';
import { Receiver } from 'app/plugins/datasource/alertmanager/types';
@ -132,7 +132,7 @@ const AmRoutes: FC = () => {
);
};
export default AmRoutes;
export default withErrorBoundary(AmRoutes, { style: 'page' });
const getStyles = (theme: GrafanaTheme2) => ({
break: css`

View File

@ -1,4 +1,4 @@
import { Alert, LoadingPlaceholder } from '@grafana/ui';
import { Alert, LoadingPlaceholder, withErrorBoundary } from '@grafana/ui';
import React, { FC, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { Redirect, Route, RouteChildrenProps, Switch, useLocation } from 'react-router-dom';
@ -104,4 +104,4 @@ const Receivers: FC = () => {
);
};
export default Receivers;
export default withErrorBoundary(Receivers, { style: 'page' });

View File

@ -1,6 +1,6 @@
import { css } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data';
import { Alert, LinkButton, LoadingPlaceholder, useStyles2 } from '@grafana/ui';
import { Alert, LinkButton, LoadingPlaceholder, useStyles2, withErrorBoundary } from '@grafana/ui';
import Page from 'app/core/components/Page/Page';
import { contextSrv } from 'app/core/services/context_srv';
import { useCleanup } from 'app/core/hooks/useCleanup';
@ -82,4 +82,4 @@ const warningStyles = (theme: GrafanaTheme2) => ({
`,
});
export default RuleEditor;
export default withErrorBoundary(RuleEditor, { style: 'page' });

View File

@ -1,7 +1,7 @@
import { DataSourceInstanceSettings, GrafanaTheme, urlUtil } from '@grafana/data';
import { useStyles, ButtonGroup, ToolbarButton, Alert, LinkButton } from '@grafana/ui';
import { useStyles, ButtonGroup, ToolbarButton, Alert, LinkButton, withErrorBoundary } from '@grafana/ui';
import { SerializedError } from '@reduxjs/toolkit';
import React, { FC, useEffect, useMemo } from 'react';
import React, { useEffect, useMemo } from 'react';
import { useDispatch } from 'react-redux';
import { AlertingPageWrapper } from './components/AlertingPageWrapper';
import { NoRulesSplash } from './components/rules/NoRulesCTA';
@ -25,7 +25,8 @@ const VIEWS = {
state: RuleListStateView,
};
export const RuleList: FC = () => {
export const RuleList = withErrorBoundary(
() => {
const dispatch = useDispatch();
const styles = useStyles(getStyles);
const rulesDataSourceNames = useMemo(getAllRulesSourceNames, []);
@ -144,7 +145,9 @@ export const RuleList: FC = () => {
{haveResults && <ViewComponent namespaces={filteredNamespaces} />}
</AlertingPageWrapper>
);
};
},
{ style: 'page' }
);
const getStyles = (theme: GrafanaTheme) => ({
break: css`

View File

@ -1,5 +1,5 @@
import React, { FC, useEffect, useCallback } from 'react';
import { Alert, LoadingPlaceholder } from '@grafana/ui';
import { Alert, LoadingPlaceholder, withErrorBoundary } from '@grafana/ui';
import { useDispatch } from 'react-redux';
import { Redirect, Route, RouteChildrenProps, Switch, useLocation } from 'react-router-dom';
@ -93,4 +93,4 @@ const Silences: FC = () => {
);
};
export default Silences;
export default withErrorBoundary(Silences, { style: 'page' });