2020-06-26 19:08:29 +02:00
|
|
|
import React, { useState } from 'react';
|
2020-06-26 15:26:50 +02:00
|
|
|
import { ErrorBoundary as ReactErrorBoundary } from 'react-error-boundary';
|
2022-11-17 02:27:24 +01:00
|
|
|
import { Icon, IconButton } from '@ohif/ui';
|
2020-06-26 17:38:10 +02:00
|
|
|
import PropTypes from 'prop-types';
|
2020-06-26 19:08:29 +02:00
|
|
|
import Modal from '../Modal';
|
2020-06-26 15:26:50 +02:00
|
|
|
|
|
|
|
|
const isProduction = process.env.NODE_ENV === 'production';
|
|
|
|
|
|
2022-11-17 02:27:24 +01:00
|
|
|
const DefaultFallback = ({
|
|
|
|
|
error,
|
|
|
|
|
context,
|
|
|
|
|
resetErrorBoundary,
|
|
|
|
|
fallbackRoute,
|
|
|
|
|
}) => {
|
|
|
|
|
const [showDetails, setShowDetails] = useState(false);
|
2020-06-26 19:08:29 +02:00
|
|
|
const title = `Something went wrong${!isProduction && ` in ${context}`}.`;
|
|
|
|
|
const subtitle = `Sorry, something went wrong there. Try again.`;
|
2020-06-26 15:26:50 +02:00
|
|
|
return (
|
2020-06-26 19:08:29 +02:00
|
|
|
<div className="ErrorFallback bg-primary-dark w-full h-full" role="alert">
|
|
|
|
|
<p className="text-primary-light text-xl">{title}</p>
|
|
|
|
|
<p className="text-primary-light text-base">{subtitle}</p>
|
|
|
|
|
{!isProduction && (
|
2022-11-17 02:27:24 +01:00
|
|
|
<div className="rounded-md bg-secondary-dark p-5 mt-5 font-mono space-y-2">
|
|
|
|
|
<p className="text-primary-light">Context: {context}</p>
|
|
|
|
|
<p className="text-primary-light">Error Message: {error.message}</p>
|
|
|
|
|
|
|
|
|
|
<IconButton
|
|
|
|
|
variant="contained"
|
|
|
|
|
color="inherit"
|
|
|
|
|
size="initial"
|
|
|
|
|
className="text-primary-active"
|
|
|
|
|
onClick={() => setShowDetails(!showDetails)}
|
|
|
|
|
>
|
|
|
|
|
<React.Fragment>
|
|
|
|
|
<div>{'Stack Trace'}</div>
|
|
|
|
|
<Icon width="15px" height="15px" name="chevron-down" />
|
|
|
|
|
</React.Fragment>
|
|
|
|
|
</IconButton>
|
|
|
|
|
|
|
|
|
|
{showDetails && (
|
|
|
|
|
<p className="px-4 text-primary-light">Stack: {error.stack}</p>
|
|
|
|
|
)}
|
2020-06-26 19:08:29 +02:00
|
|
|
</div>
|
2020-06-26 15:26:50 +02:00
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2022-11-17 02:27:24 +01:00
|
|
|
const noop = () => {};
|
2020-06-26 19:08:29 +02:00
|
|
|
|
|
|
|
|
DefaultFallback.propTypes = {
|
|
|
|
|
error: PropTypes.object.isRequired,
|
|
|
|
|
resetErrorBoundary: PropTypes.func,
|
|
|
|
|
componentStack: PropTypes.string,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
DefaultFallback.defaultProps = {
|
2022-11-17 02:27:24 +01:00
|
|
|
resetErrorBoundary: noop,
|
2020-06-26 15:26:50 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const ErrorBoundary = ({
|
2020-06-26 19:08:29 +02:00
|
|
|
context,
|
|
|
|
|
onReset,
|
|
|
|
|
onError,
|
|
|
|
|
fallbackComponent: FallbackComponent,
|
|
|
|
|
children,
|
|
|
|
|
fallbackRoute,
|
2022-11-17 02:27:24 +01:00
|
|
|
isPage,
|
2020-06-26 15:26:50 +02:00
|
|
|
}) => {
|
2020-06-26 19:08:29 +02:00
|
|
|
const [isOpen, setIsOpen] = useState(true);
|
|
|
|
|
|
2020-06-26 15:26:50 +02:00
|
|
|
const onErrorHandler = (error, componentStack) => {
|
2020-06-26 19:08:29 +02:00
|
|
|
console.error(`${context} Error Boundary`, error, componentStack, context);
|
|
|
|
|
onError(error, componentStack, context);
|
2020-06-26 15:26:50 +02:00
|
|
|
};
|
|
|
|
|
|
2020-06-26 19:08:29 +02:00
|
|
|
const onResetHandler = (...args) => onReset(...args);
|
|
|
|
|
|
2022-11-17 02:27:24 +01:00
|
|
|
const withModal = Component => props => (
|
2020-06-26 19:08:29 +02:00
|
|
|
<Modal
|
|
|
|
|
closeButton
|
|
|
|
|
shouldCloseOnEsc
|
|
|
|
|
isOpen={isOpen}
|
|
|
|
|
title={'Something went wrong'}
|
|
|
|
|
onClose={() => {
|
|
|
|
|
setIsOpen(false);
|
2020-08-14 20:25:08 +02:00
|
|
|
if (fallbackRoute && typeof window !== 'undefined') {
|
2020-06-26 19:08:29 +02:00
|
|
|
window.location = fallbackRoute;
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Component {...props} />
|
|
|
|
|
</Modal>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const Fallback = isPage ? FallbackComponent : withModal(FallbackComponent);
|
2020-06-26 15:26:50 +02:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<ReactErrorBoundary
|
2020-06-26 19:08:29 +02:00
|
|
|
fallbackRender={props => (
|
2022-11-17 02:27:24 +01:00
|
|
|
<Fallback {...props} context={context} fallbackRoute={fallbackRoute} />
|
2020-06-26 19:08:29 +02:00
|
|
|
)}
|
2020-06-26 15:26:50 +02:00
|
|
|
onReset={onResetHandler}
|
|
|
|
|
onError={onErrorHandler}
|
|
|
|
|
>
|
|
|
|
|
{children}
|
|
|
|
|
</ReactErrorBoundary>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2020-06-26 19:08:29 +02:00
|
|
|
ErrorBoundary.propTypes = {
|
|
|
|
|
context: PropTypes.string,
|
|
|
|
|
onReset: PropTypes.func,
|
|
|
|
|
onError: PropTypes.func,
|
|
|
|
|
fallbackComponent: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
|
|
|
|
|
children: PropTypes.node.isRequired,
|
2022-11-17 02:27:24 +01:00
|
|
|
fallbackRoute: PropTypes.string,
|
2020-06-26 19:08:29 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ErrorBoundary.defaultProps = {
|
|
|
|
|
context: 'OHIF',
|
|
|
|
|
onReset: noop,
|
|
|
|
|
onError: noop,
|
|
|
|
|
fallbackComponent: DefaultFallback,
|
2022-11-17 02:27:24 +01:00
|
|
|
fallbackRoute: null,
|
2020-06-26 19:08:29 +02:00
|
|
|
};
|
|
|
|
|
|
2020-06-26 15:26:50 +02:00
|
|
|
export default ErrorBoundary;
|