feat: enhanced error handling ui (#3021)
* feat: better error handling * better error handling * try to fix netlify build * remove new line
This commit is contained in:
parent
83ea228d86
commit
eddd510a35
@ -8,14 +8,6 @@ import { utils } from '@ohif/core';
|
||||
|
||||
const { downloadCSVReport } = utils;
|
||||
|
||||
// tools with measurements to display inside the panel
|
||||
const MEASUREMENT_TOOLS = [
|
||||
'EllipticalROI',
|
||||
'RectangleROI',
|
||||
'Length',
|
||||
'Bidirectional',
|
||||
];
|
||||
|
||||
export default function PanelMeasurementTable({
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
@ -185,12 +177,8 @@ PanelMeasurementTable.propTypes = {
|
||||
|
||||
function _getMappedMeasurements(MeasurementService) {
|
||||
const measurements = MeasurementService.getMeasurements();
|
||||
// filter out measurements whose toolName is not in MEASUREMENT_TOOLS
|
||||
const measurementTools = measurements.filter(measurement =>
|
||||
MEASUREMENT_TOOLS.includes(measurement.toolName)
|
||||
);
|
||||
|
||||
const mappedMeasurements = measurementTools.map((m, index) =>
|
||||
const mappedMeasurements = measurements.map((m, index) =>
|
||||
_mapMeasurementToDisplay(m, index, MeasurementService.VALUE_TYPES)
|
||||
);
|
||||
|
||||
|
||||
@ -122,10 +122,29 @@ function ViewerLayout({
|
||||
};
|
||||
}, []);
|
||||
|
||||
const getPanelData = id => {
|
||||
const getComponent = id => {
|
||||
const entry = extensionManager.getModuleEntry(id);
|
||||
// TODO, not sure why sidepanel content has to be JSX, and not a children prop?
|
||||
const content = entry.component;
|
||||
|
||||
if (!entry) {
|
||||
throw new Error(
|
||||
`${id} is not a valid entry for an extension module, please check your configuration or make sure the extension is registered.`
|
||||
);
|
||||
}
|
||||
|
||||
let content;
|
||||
if (entry && entry.component) {
|
||||
content = entry.component;
|
||||
} else {
|
||||
throw new Error(
|
||||
`No component found from extension ${id}. Check the reference string to the extension in your Mode configuration`
|
||||
);
|
||||
}
|
||||
|
||||
return { entry, content };
|
||||
};
|
||||
|
||||
const getPanelData = id => {
|
||||
const { content, entry } = getComponent(id);
|
||||
|
||||
return {
|
||||
iconName: entry.iconName,
|
||||
@ -156,7 +175,7 @@ function ViewerLayout({
|
||||
}, [HangingProtocolService]);
|
||||
|
||||
const getViewportComponentData = viewportComponent => {
|
||||
const entry = extensionManager.getModuleEntry(viewportComponent.namespace);
|
||||
const { entry } = getComponent(viewportComponent.namespace);
|
||||
|
||||
return {
|
||||
component: entry.component,
|
||||
|
||||
@ -498,8 +498,7 @@ class MeasurementService {
|
||||
measurement.source = source;
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to map '${sourceInfo}' measurement for annotationType ${annotationType}:`,
|
||||
error.message
|
||||
`Failed to map '${sourceInfo}' measurement for annotationType ${annotationType}: ${error.message}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1,11 +1,18 @@
|
||||
import React, { useState } from 'react';
|
||||
import { ErrorBoundary as ReactErrorBoundary } from 'react-error-boundary';
|
||||
import { Icon, IconButton } from '@ohif/ui';
|
||||
import PropTypes from 'prop-types';
|
||||
import Modal from '../Modal';
|
||||
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
const DefaultFallback = ({ error, componentStack, context, resetErrorBoundary, fallbackRoute }) => {
|
||||
const DefaultFallback = ({
|
||||
error,
|
||||
context,
|
||||
resetErrorBoundary,
|
||||
fallbackRoute,
|
||||
}) => {
|
||||
const [showDetails, setShowDetails] = useState(false);
|
||||
const title = `Something went wrong${!isProduction && ` in ${context}`}.`;
|
||||
const subtitle = `Sorry, something went wrong there. Try again.`;
|
||||
return (
|
||||
@ -13,17 +20,33 @@ const DefaultFallback = ({ error, componentStack, context, resetErrorBoundary, f
|
||||
<p className="text-primary-light text-xl">{title}</p>
|
||||
<p className="text-primary-light text-base">{subtitle}</p>
|
||||
{!isProduction && (
|
||||
<div className="rounded-md bg-secondary-dark p-5 mt-5">
|
||||
<pre className="text-primary-light">Context: {context}</pre>
|
||||
<pre className="text-primary-light">Error Message: {error.message}</pre>
|
||||
<pre className="text-primary-light">Stack: {componentStack}</pre>
|
||||
<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>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const noop = () => { };
|
||||
const noop = () => {};
|
||||
|
||||
DefaultFallback.propTypes = {
|
||||
error: PropTypes.object.isRequired,
|
||||
@ -32,7 +55,7 @@ DefaultFallback.propTypes = {
|
||||
};
|
||||
|
||||
DefaultFallback.defaultProps = {
|
||||
resetErrorBoundary: noop
|
||||
resetErrorBoundary: noop,
|
||||
};
|
||||
|
||||
const ErrorBoundary = ({
|
||||
@ -42,7 +65,7 @@ const ErrorBoundary = ({
|
||||
fallbackComponent: FallbackComponent,
|
||||
children,
|
||||
fallbackRoute,
|
||||
isPage
|
||||
isPage,
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
|
||||
@ -53,7 +76,7 @@ const ErrorBoundary = ({
|
||||
|
||||
const onResetHandler = (...args) => onReset(...args);
|
||||
|
||||
const withModal = (Component) => props => (
|
||||
const withModal = Component => props => (
|
||||
<Modal
|
||||
closeButton
|
||||
shouldCloseOnEsc
|
||||
@ -75,11 +98,7 @@ const ErrorBoundary = ({
|
||||
return (
|
||||
<ReactErrorBoundary
|
||||
fallbackRender={props => (
|
||||
<Fallback
|
||||
{...props}
|
||||
context={context}
|
||||
fallbackRoute={fallbackRoute}
|
||||
/>
|
||||
<Fallback {...props} context={context} fallbackRoute={fallbackRoute} />
|
||||
)}
|
||||
onReset={onResetHandler}
|
||||
onError={onErrorHandler}
|
||||
@ -95,7 +114,7 @@ ErrorBoundary.propTypes = {
|
||||
onError: PropTypes.func,
|
||||
fallbackComponent: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
|
||||
children: PropTypes.node.isRequired,
|
||||
fallbackRoute: PropTypes.string
|
||||
fallbackRoute: PropTypes.string,
|
||||
};
|
||||
|
||||
ErrorBoundary.defaultProps = {
|
||||
@ -103,7 +122,7 @@ ErrorBoundary.defaultProps = {
|
||||
onReset: noop,
|
||||
onError: noop,
|
||||
fallbackComponent: DefaultFallback,
|
||||
fallbackRoute: null
|
||||
fallbackRoute: null,
|
||||
};
|
||||
|
||||
export default ErrorBoundary;
|
||||
|
||||
@ -201,6 +201,14 @@ const ICONS = {
|
||||
'old-stop': oldStop,
|
||||
};
|
||||
|
||||
function addIcon(iconName, iconSVG) {
|
||||
if (ICONS[iconName]) {
|
||||
console.warn(`Icon ${iconName} already exists.`);
|
||||
}
|
||||
|
||||
ICONS[iconName] = iconSVG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the matching SVG Icon as a React Component.
|
||||
* Results in an inlined SVG Element. If there's no match,
|
||||
@ -214,4 +222,4 @@ export default function getIcon(key, props) {
|
||||
return React.createElement(ICONS[key], props);
|
||||
}
|
||||
|
||||
export { getIcon, ICONS };
|
||||
export { getIcon, ICONS, addIcon };
|
||||
|
||||
@ -8,7 +8,7 @@ import Dialog from './Dialog';
|
||||
import Dropdown from './Dropdown';
|
||||
import EmptyStudies from './EmptyStudies';
|
||||
import ErrorBoundary from './ErrorBoundary';
|
||||
import Icon from './Icon';
|
||||
import Icon, { addIcon } from './Icon';
|
||||
import IconButton from './IconButton';
|
||||
import Input from './Input';
|
||||
import InputDateRange from './InputDateRange';
|
||||
@ -89,6 +89,7 @@ export {
|
||||
ExpandableToolbarButton,
|
||||
ListMenu,
|
||||
Icon,
|
||||
addIcon,
|
||||
IconButton,
|
||||
Input,
|
||||
InputDateRange,
|
||||
|
||||
@ -107,7 +107,7 @@ export {
|
||||
} from './components';
|
||||
|
||||
/** These are mostly used in the docs */
|
||||
export { getIcon, ICONS } from './components/Icon/getIcon';
|
||||
export { getIcon, ICONS, addIcon } from './components/Icon/getIcon';
|
||||
export { BackgroundColor } from './pages/Colors/BackgroundColor';
|
||||
export { ModalComponent } from './contextProviders/ModalComponent';
|
||||
export { Types };
|
||||
|
||||
@ -1 +1 @@
|
||||
3.7
|
||||
3.8
|
||||
Loading…
Reference in New Issue
Block a user