Added high level ErrorBoundary

This commit is contained in:
Tom Moor
2017-11-12 16:08:55 -08:00
parent 0909dc61ff
commit ee89fb852b
4 changed files with 106 additions and 52 deletions

View File

@@ -15,6 +15,7 @@ type Props = {
readOnly: boolean, readOnly: boolean,
component?: string, component?: string,
attributes: Object, attributes: Object,
className?: string,
}; };
function Heading(props: Props) { function Heading(props: Props) {
@@ -26,6 +27,7 @@ function Heading(props: Props) {
readOnly, readOnly,
children, children,
component = 'h1', component = 'h1',
className,
attributes, attributes,
} = props; } = props;
const parentIsDocument = parent instanceof Document; const parentIsDocument = parent instanceof Document;
@@ -40,7 +42,7 @@ function Heading(props: Props) {
emoji && title.match(new RegExp(`^${emoji}\\s`)); emoji && title.match(new RegExp(`^${emoji}\\s`));
return ( return (
<Component {...attributes} id={slugish}> <Component {...attributes} id={slugish} className={className}>
<Wrapper hasEmoji={startsWithEmojiAndSpace}>{children}</Wrapper> <Wrapper hasEmoji={startsWithEmojiAndSpace}>{children}</Wrapper>
{showPlaceholder && ( {showPlaceholder && (
<Placeholder contentEditable={false}> <Placeholder contentEditable={false}>

View File

@@ -0,0 +1,42 @@
// @flow
import React, { Component } from 'react';
import { observer } from 'mobx-react';
import { observable } from 'mobx';
import CenteredContent from 'components/CenteredContent';
import PageTitle from 'components/PageTitle';
@observer
class ErrorBoundary extends Component {
@observable error: boolean = false;
componentDidCatch(error: Error, info: Object) {
this.error = true;
// Error handler is often blocked by the browser
if (window.Bugsnag) {
Bugsnag.notifyException(error, { react: info });
}
}
handleReload = () => {
window.location.reload();
};
render() {
if (this.error) {
return (
<CenteredContent>
<PageTitle title="Unknown Error" />
<h1>Something went wrong</h1>
<p>
An unrecoverable error occurred. Please try{' '}
<a onClick={this.handleReload}>reloading</a>.
</p>
</CenteredContent>
);
}
return this.props.children;
}
}
export default ErrorBoundary;

View File

@@ -0,0 +1,3 @@
// @flow
import ErrorBoundary from './ErrorBoundary';
export default ErrorBoundary;

View File

@@ -28,6 +28,7 @@ import Flatpage from 'scenes/Flatpage';
import ErrorAuth from 'scenes/ErrorAuth'; import ErrorAuth from 'scenes/ErrorAuth';
import Error404 from 'scenes/Error404'; import Error404 from 'scenes/Error404';
import ErrorBoundary from 'components/ErrorBoundary';
import ScrollToTop from 'components/ScrollToTop'; import ScrollToTop from 'components/ScrollToTop';
import Layout from 'components/Layout'; import Layout from 'components/Layout';
import RouteSidebarHidden from 'components/RouteSidebarHidden'; import RouteSidebarHidden from 'components/RouteSidebarHidden';
@@ -101,62 +102,68 @@ globalStyles();
render( render(
<div style={{ display: 'flex', flex: 1, height: '100%' }}> <div style={{ display: 'flex', flex: 1, height: '100%' }}>
<Provider {...stores}> <ErrorBoundary>
<Router> <Provider {...stores}>
<ScrollToTop> <Router>
<Switch> <ScrollToTop>
<Route exact path="/" component={Home} /> <Switch>
<Route exact path="/" component={Home} />
<Route exact path="/auth/slack" component={SlackAuth} /> <Route exact path="/auth/slack" component={SlackAuth} />
<Route exact path="/auth/slack/commands" component={SlackAuth} /> <Route exact path="/auth/slack/commands" component={SlackAuth} />
<Route exact path="/auth/error" component={ErrorAuth} /> <Route exact path="/auth/error" component={ErrorAuth} />
<Auth> <Auth>
<Layout> <Layout>
<Switch> <Switch>
<Route exact path="/dashboard" component={Dashboard} /> <Route exact path="/dashboard" component={Dashboard} />
<Route exact path="/starred" component={Starred} /> <Route exact path="/starred" component={Starred} />
<Route exact path="/collections/:id" component={Collection} /> <Route
<Route exact
exact path="/collections/:id"
path={`/d/${matchDocumentSlug}`} component={Collection}
component={RedirectDocument} />
/> <Route
<Route exact
exact path={`/d/${matchDocumentSlug}`}
path={`/doc/${matchDocumentSlug}`} component={RedirectDocument}
component={Document} />
/> <Route
<Route exact
exact path={`/doc/${matchDocumentSlug}`}
path={`/doc/${matchDocumentSlug}/move`} component={Document}
component={Document} />
/> <Route
exact
path={`/doc/${matchDocumentSlug}/move`}
component={Document}
/>
<Route exact path="/search" component={Search} /> <Route exact path="/search" component={Search} />
<Route exact path="/search/:query" component={Search} /> <Route exact path="/search/:query" component={Search} />
<Route exact path="/developers" component={Api} /> <Route exact path="/developers" component={Api} />
<Route path="/404" component={Error404} /> <Route path="/404" component={Error404} />
<RouteSidebarHidden <RouteSidebarHidden
exact exact
path={`/doc/${matchDocumentSlug}/edit`} path={`/doc/${matchDocumentSlug}/edit`}
component={Document} component={Document}
/> />
<RouteSidebarHidden <RouteSidebarHidden
exact exact
path="/collections/:id/new" path="/collections/:id/new"
component={DocumentNew} component={DocumentNew}
/> />
<Route component={notFoundSearch} /> <Route component={notFoundSearch} />
</Switch> </Switch>
</Layout> </Layout>
</Auth> </Auth>
</Switch> </Switch>
</ScrollToTop> </ScrollToTop>
</Router> </Router>
</Provider> </Provider>
</ErrorBoundary>
{DevTools && <DevTools position={{ bottom: 0, right: 0 }} />} {DevTools && <DevTools position={{ bottom: 0, right: 0 }} />}
</div>, </div>,
document.getElementById('root') document.getElementById('root')