From 538704b62069150b3aa2e2f9467419148ed8f191 Mon Sep 17 00:00:00 2001 From: Samed <sbalcioglu@graphpolaris.com> Date: Tue, 12 Nov 2024 11:35:20 +0000 Subject: [PATCH] feat: stack trace interface in error boundaries --- .../errorBoundary/ErrorBoundary.tsx | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/libs/shared/lib/components/errorBoundary/ErrorBoundary.tsx b/libs/shared/lib/components/errorBoundary/ErrorBoundary.tsx index 14908635c..e3169344e 100644 --- a/libs/shared/lib/components/errorBoundary/ErrorBoundary.tsx +++ b/libs/shared/lib/components/errorBoundary/ErrorBoundary.tsx @@ -1,20 +1,27 @@ import React, { Component, ErrorInfo, ReactNode } from 'react'; import { Button } from '../buttons'; +import { useAppDispatch } from '@graphpolaris/shared/lib/data-access'; +import { addInfo } from '@graphpolaris/shared/lib/data-access/store/configSlice'; interface ErrorBoundaryProps { children: ReactNode; fallback?: ReactNode; onError?: (error: Error, errorInfo: ErrorInfo) => void; + dispatch?: ReturnType<typeof useAppDispatch>; } interface ErrorBoundaryState { hasError: boolean; + error?: Error; + errorInfo?: ErrorInfo; } class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> { constructor(props: ErrorBoundaryProps) { super(props); this.state = { hasError: false }; + this.handleRetry = this.handleRetry.bind(this); + this.handleCopyError = this.handleCopyError.bind(this); } static getDerivedStateFromError(): ErrorBoundaryState { @@ -22,20 +29,48 @@ class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> { } componentDidCatch(error: Error, errorInfo: ErrorInfo) { + this.setState({ error, errorInfo }); if (this.props.onError) { this.props.onError(error, errorInfo); } } handleRetry() { - this.setState({ hasError: false }); + this.setState({ hasError: false, error: undefined, errorInfo: undefined }); + } + + handleCopyError() { + const errorText = `Error: ${this.state.error?.toString()}\n\nStack trace: ${this.state.errorInfo?.componentStack}`; + navigator.clipboard.writeText(errorText); + if (this.props.dispatch) { + this.props.dispatch(addInfo('Stack trace copied to clipboard')); + } } render() { if (this.state.hasError) { return ( - <div> + <div className="flex flex-col w-full h-full"> {this.props.fallback || <div>Something went wrong. Please try again later.</div>} + {import.meta.env.GRAPHPOLARIS_VERSION === 'dev' && ( + <div className="overflow-auto max-h-[500px] p-2"> + <div className="flex justify-end mb-2"> + <Button + label="Copy Stack Trace" + variant="outline" + variantType="secondary" + size="xs" + onClick={this.handleCopyError} + /> + </div> + <pre className="whitespace-pre-wrap break-words text-sm"> + {this.state.error?.toString()} + </pre> + <pre className="whitespace-pre-wrap break-words text-sm mt-2"> + {this.state.errorInfo?.componentStack} + </pre> + </div> + )} <Button label="Retry now" variant="outline" variantType="primary" onClick={this.handleRetry} /> </div> ); @@ -45,4 +80,9 @@ class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> { } } -export { ErrorBoundary }; +function ErrorBoundaryWithDispatch(props: Omit<ErrorBoundaryProps, 'dispatch'>) { + const dispatch = useAppDispatch(); + return <ErrorBoundary {...props} dispatch={dispatch} />; +} + +export { ErrorBoundaryWithDispatch as ErrorBoundary }; \ No newline at end of file -- GitLab