mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-07-06 15:47:48 +02:00
Merged in feature/error_boundary (pull request #217)
Feature/error boundary
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
// @flow
|
||||
import * as React from "react";
|
||||
import ErrorNotification from "./ErrorNotification";
|
||||
|
||||
type Props = {
|
||||
fallback?: React.ComponentType<any>,
|
||||
children: React.Node
|
||||
};
|
||||
|
||||
type ErrorInfo = {
|
||||
componentStack: string
|
||||
};
|
||||
|
||||
type State = {
|
||||
error?: Error,
|
||||
errorInfo?: ErrorInfo
|
||||
};
|
||||
|
||||
class ErrorBoundary extends React.Component<Props,State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||
// Catch errors in any components below and re-render with error message
|
||||
this.setState({
|
||||
error,
|
||||
errorInfo
|
||||
});
|
||||
}
|
||||
|
||||
renderError = () => {
|
||||
let FallbackComponent = this.props.fallback;
|
||||
if (!FallbackComponent) {
|
||||
FallbackComponent = ErrorNotification;
|
||||
}
|
||||
|
||||
return <FallbackComponent {...this.state} />;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { error } = this.state;
|
||||
if (error) {
|
||||
return this.renderError();
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
export default ErrorBoundary;
|
||||
@@ -27,6 +27,7 @@ export { default as Autocomplete} from "./Autocomplete";
|
||||
export { default as BranchSelector } from "./BranchSelector";
|
||||
export { default as MarkdownView } from "./MarkdownView";
|
||||
export { default as SyntaxHighlighter } from "./SyntaxHighlighter";
|
||||
export { default as ErrorBoundary } from "./ErrorBoundary";
|
||||
|
||||
export { apiClient } from "./apiclient.js";
|
||||
export * from "./errors";
|
||||
|
||||
@@ -7,6 +7,7 @@ import Subtitle from "./Subtitle";
|
||||
import injectSheet from "react-jss";
|
||||
import classNames from "classnames";
|
||||
import PageActions from "./PageActions";
|
||||
import ErrorBoundary from "../ErrorBoundary";
|
||||
|
||||
type Props = {
|
||||
title?: string,
|
||||
@@ -34,10 +35,13 @@ class Page extends React.Component<Props> {
|
||||
<section className="section">
|
||||
<div className="container">
|
||||
{this.renderPageHeader()}
|
||||
<ErrorNotification error={error} />
|
||||
{this.renderContent()}
|
||||
<ErrorBoundary>
|
||||
<ErrorNotification error={error}/>
|
||||
{this.renderContent()}
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
@@ -64,15 +68,15 @@ class Page extends React.Component<Props> {
|
||||
}
|
||||
});
|
||||
let underline = pageActionsExists ? (
|
||||
<hr className="header-with-actions" />
|
||||
<hr className="header-with-actions"/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<Title title={title} />
|
||||
<Subtitle subtitle={subtitle} />
|
||||
<Title title={title}/>
|
||||
<Subtitle subtitle={subtitle}/>
|
||||
</div>
|
||||
{pageActions}
|
||||
</div>
|
||||
@@ -88,7 +92,7 @@ class Page extends React.Component<Props> {
|
||||
return null;
|
||||
}
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
return <Loading/>;
|
||||
}
|
||||
|
||||
let content = [];
|
||||
|
||||
@@ -5,7 +5,7 @@ import { connect } from "react-redux";
|
||||
import { translate } from "react-i18next";
|
||||
import { withRouter } from "react-router-dom";
|
||||
|
||||
import { Loading, ErrorPage } from "@scm-manager/ui-components";
|
||||
import { Loading, ErrorBoundary } from "@scm-manager/ui-components";
|
||||
import {
|
||||
fetchIndexResources,
|
||||
getFetchIndexResourcesFailure,
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
import PluginLoader from "./PluginLoader";
|
||||
import type { IndexResources } from "@scm-manager/ui-types";
|
||||
import ScrollToTop from "./ScrollToTop";
|
||||
import IndexErrorPage from "./IndexErrorPage";
|
||||
|
||||
type Props = {
|
||||
error: Error,
|
||||
@@ -55,25 +56,21 @@ class Index extends Component<Props, State> {
|
||||
const { pluginsLoaded } = this.state;
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<ErrorPage
|
||||
title={t("app.error.title")}
|
||||
subtitle={t("app.error.subtitle")}
|
||||
error={error}
|
||||
/>
|
||||
);
|
||||
return <IndexErrorPage error={error}/>;
|
||||
} else if (loading || !indexResources) {
|
||||
return <Loading />;
|
||||
} else {
|
||||
return (
|
||||
<ScrollToTop>
|
||||
<PluginLoader
|
||||
loaded={pluginsLoaded}
|
||||
callback={this.pluginLoaderCallback}
|
||||
>
|
||||
<App />
|
||||
</PluginLoader>
|
||||
</ScrollToTop>
|
||||
<ErrorBoundary fallback={IndexErrorPage}>
|
||||
<ScrollToTop>
|
||||
<PluginLoader
|
||||
loaded={pluginsLoaded}
|
||||
callback={this.pluginLoaderCallback}
|
||||
>
|
||||
<App />
|
||||
</PluginLoader>
|
||||
</ScrollToTop>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
26
scm-ui/src/containers/IndexErrorPage.js
Normal file
26
scm-ui/src/containers/IndexErrorPage.js
Normal file
@@ -0,0 +1,26 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate, type TFunction } from "react-i18next";
|
||||
import { ErrorPage } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
error: Error,
|
||||
t: TFunction
|
||||
}
|
||||
|
||||
class IndexErrorPage extends React.Component<Props> {
|
||||
|
||||
render() {
|
||||
const { error, t } = this.props;
|
||||
return (
|
||||
<ErrorPage
|
||||
title={t("app.error.title")}
|
||||
subtitle={t("app.error.subtitle")}
|
||||
error={error}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default translate("commons")(IndexErrorPage);
|
||||
@@ -9,7 +9,7 @@ import Users from "../users/containers/Users";
|
||||
import Login from "../containers/Login";
|
||||
import Logout from "../containers/Logout";
|
||||
|
||||
import { ProtectedRoute } from "@scm-manager/ui-components";
|
||||
import {ProtectedRoute} from "@scm-manager/ui-components";
|
||||
import {binder, ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
|
||||
import AddUser from "../users/containers/AddUser";
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import RepositoryDetailTable from "./RepositoryDetailTable";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
|
||||
type Props = {
|
||||
repository: Repository
|
||||
};
|
||||
|
||||
class RepositoryDetails extends React.Component<Props> {
|
||||
render() {
|
||||
const { repository } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<RepositoryDetailTable repository={repository} />
|
||||
<hr />
|
||||
<div className="content">
|
||||
<ExtensionPoint
|
||||
name="repos.repository-details.information"
|
||||
renderAll={true}
|
||||
props={{ repository }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RepositoryDetails;
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import RepositoryDetailTable from "./RepositoryDetailTable";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
|
||||
type Props = {
|
||||
repository: Repository
|
||||
};
|
||||
|
||||
class RepositoryDetails extends React.Component<Props> {
|
||||
render() {
|
||||
const { repository } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<RepositoryDetailTable repository={repository}/>
|
||||
<hr/>
|
||||
<div className="content">
|
||||
<ExtensionPoint
|
||||
name="repos.repository-details.information"
|
||||
renderAll={true}
|
||||
props={{ repository }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RepositoryDetails;
|
||||
|
||||
Reference in New Issue
Block a user