fix(ErrorBoundary): Allow for details to be shown in production. (#5504)

Added showErrorDetails to config to allow for details to be shown in various runtime environments.
This commit is contained in:
Joe Boccanfuso 2025-10-17 08:15:13 -04:00 committed by GitHub
parent cf8f4aa120
commit 4620cc3acf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 33 additions and 9 deletions

View File

@ -25,6 +25,7 @@ window.config = {
// above, the number of requests can be go a lot higher. // above, the number of requests can be go a lot higher.
prefetch: 25, prefetch: 25,
}, },
showErrorDetails: 'always', // 'always', 'dev', 'production'
// filterQueryParam: false, // filterQueryParam: false,
// Defines multi-monitor layouts // Defines multi-monitor layouts
multimonitor: [ multimonitor: [

View File

@ -128,11 +128,17 @@ const createRoutes = ({
]; ];
function RouteWithErrorBoundary({ route, ...rest }) { function RouteWithErrorBoundary({ route, ...rest }) {
const [appConfig] = useAppConfig();
const { showErrorDetails } = appConfig;
history.navigate = useNavigate(); history.navigate = useNavigate();
// eslint-disable-next-line react/jsx-props-no-spreading // eslint-disable-next-line react/jsx-props-no-spreading
return ( return (
<ErrorBoundary context={`Route ${route.path}`}> <ErrorBoundary
context={`Route ${route.path}`}
showErrorDetails={showErrorDetails}
>
<route.children <route.children
{...rest} {...rest}
{...route.props} {...route.props}

View File

@ -196,6 +196,7 @@ if auth headers are used, a preflight request is required.
- `activateViewportBeforeInteraction`: (default to true), if set to false, tools can be used directly without the need to click and activate the viewport. - `activateViewportBeforeInteraction`: (default to true), if set to false, tools can be used directly without the need to click and activate the viewport.
- `autoPlayCine`: (default to false), if set to true, data sets with the DICOM frame time tag (i.e. (0018,1063)) will auto play when displayed - `autoPlayCine`: (default to false), if set to true, data sets with the DICOM frame time tag (i.e. (0018,1063)) will auto play when displayed
- `addWindowLevelActionMenu`: (default to true), if set to false, the window level action menu item is NOT added to the viewport action corners - `addWindowLevelActionMenu`: (default to true), if set to false, the window level action menu item is NOT added to the viewport action corners
- `showErrorDetails`: determines which runtime environments can display exception and error details caught at the `ErrorBoundary`; acceptable values include: `always`, `dev`, and `production`
- `dangerouslyUseDynamicConfig`: Dynamic config allows user to pass `configUrl` query string. This allows to load config without recompiling application. If the `configUrl` query string is passed, the worklist and modes will load from the referenced json rather than the default .env config. If there is no `configUrl` path provided, the default behaviour is used and there should not be any deviation from current user experience.<br/> - `dangerouslyUseDynamicConfig`: Dynamic config allows user to pass `configUrl` query string. This allows to load config without recompiling application. If the `configUrl` query string is passed, the worklist and modes will load from the referenced json rather than the default .env config. If there is no `configUrl` path provided, the default behaviour is used and there should not be any deviation from current user experience.<br/>
Points to consider while using `dangerouslyUseDynamicConfig`:<br/> Points to consider while using `dangerouslyUseDynamicConfig`:<br/>
- User have to enable this feature by setting `dangerouslyUseDynamicConfig.enabled:true`. By default it is `false`. - User have to enable this feature by setting `dangerouslyUseDynamicConfig.enabled:true`. By default it is `false`.

View File

@ -112,10 +112,17 @@ interface ErrorBoundaryError extends Error {
stack?: string; stack?: string;
} }
enum ShowErrorDetails {
always = 'always',
dev = 'dev',
production = 'production',
}
interface DefaultFallbackProps extends FallbackProps { interface DefaultFallbackProps extends FallbackProps {
error: ErrorBoundaryError; error: ErrorBoundaryError;
context: string; context: string;
resetErrorBoundary: () => void; resetErrorBoundary: () => void;
showErrorDetails?: ShowErrorDetails;
} }
interface ErrorBoundaryProps { interface ErrorBoundaryProps {
@ -126,13 +133,21 @@ interface ErrorBoundaryProps {
children: React.ReactNode; children: React.ReactNode;
fallbackRoute?: string | null; fallbackRoute?: string | null;
isPage?: boolean; isPage?: boolean;
showErrorDetails?: ShowErrorDetails;
} }
const DefaultFallback = ({ const DefaultFallback = ({
error, error,
context, context,
resetErrorBoundary = () => {}, resetErrorBoundary = () => {},
showErrorDetails,
}: DefaultFallbackProps) => { }: DefaultFallbackProps) => {
const isShowDetailsButtonVisible =
showErrorDetails == null ||
showErrorDetails === ShowErrorDetails.always ||
(showErrorDetails === ShowErrorDetails.dev && !isProduction) ||
(showErrorDetails === ShowErrorDetails.production && isProduction);
const { t } = useTranslation('ErrorBoundary'); const { t } = useTranslation('ErrorBoundary');
const [showDetails, setShowDetails] = useState(false); const [showDetails, setShowDetails] = useState(false);
const { show } = useNotification(); const { show } = useNotification();
@ -165,17 +180,15 @@ const DefaultFallback = ({
type: 'error', type: 'error',
duration: 0, duration: 0,
id: errorId, id: errorId,
action: { action: isShowDetailsButtonVisible
? {
label: t('Show Details'), label: t('Show Details'),
onClick: () => setShowDetails(true), onClick: () => setShowDetails(true),
}, }
: undefined,
}); });
}, [error, errorTitle, subtitle, t, title, show]); }, [error, errorTitle, subtitle, t, title, show]);
if (isProduction) {
return null;
}
return ( return (
<Dialog <Dialog
open={showDetails} open={showDetails}
@ -250,6 +263,7 @@ const ErrorBoundary = ({
onError = _error => {}, onError = _error => {},
fallbackComponent: FallbackComponent = DefaultFallback, fallbackComponent: FallbackComponent = DefaultFallback,
children, children,
showErrorDetails,
}: ErrorBoundaryProps) => { }: ErrorBoundaryProps) => {
const [error, setError] = useState<ErrorBoundaryError | null>(null); const [error, setError] = useState<ErrorBoundaryError | null>(null);
@ -303,6 +317,7 @@ const ErrorBoundary = ({
<FallbackComponent <FallbackComponent
{...props} {...props}
context={context} context={context}
showErrorDetails={showErrorDetails}
/> />
)} )}
onReset={onResetHandler} onReset={onResetHandler}
@ -315,6 +330,7 @@ const ErrorBoundary = ({
error={error} error={error}
context={context} context={context}
resetErrorBoundary={() => setError(null)} resetErrorBoundary={() => setError(null)}
showErrorDetails={showErrorDetails}
/> />
)} )}
</> </>