119 lines
2.9 KiB
TypeScript
119 lines
2.9 KiB
TypeScript
/**
|
||
* External dependencies
|
||
*/
|
||
import { Component, ReactNode, ErrorInfo } from 'react';
|
||
import { __ } from '@wordpress/i18n';
|
||
import { Button } from '@wordpress/components';
|
||
import { captureException } from '@woocommerce/remote-logging';
|
||
import { bumpStat } from '@woocommerce/tracks';
|
||
/**
|
||
* Internal dependencies
|
||
*/
|
||
import './style.scss';
|
||
|
||
type ErrorBoundaryProps = {
|
||
children: ReactNode;
|
||
};
|
||
|
||
type ErrorBoundaryState = {
|
||
hasError: boolean;
|
||
error: Error | null;
|
||
errorInfo: ErrorInfo | null;
|
||
};
|
||
|
||
export class ErrorBoundary extends Component<
|
||
ErrorBoundaryProps,
|
||
ErrorBoundaryState
|
||
> {
|
||
constructor( props: ErrorBoundaryProps ) {
|
||
super( props );
|
||
this.state = { hasError: false, error: null, errorInfo: null };
|
||
}
|
||
|
||
static getDerivedStateFromError(
|
||
error: Error
|
||
): Partial< ErrorBoundaryState > {
|
||
return { hasError: true, error };
|
||
}
|
||
|
||
componentDidCatch( error: Error, errorInfo: ErrorInfo ) {
|
||
this.setState( { errorInfo } );
|
||
|
||
bumpStat( 'error', 'unhandled-js-error-during-render' );
|
||
|
||
// Limit the component stack to 10 calls so we don't send too much data.
|
||
const componentStack = errorInfo.componentStack
|
||
.trim()
|
||
.split( '\n' )
|
||
.slice( 0, 10 )
|
||
.map( ( line ) => line.trim() );
|
||
|
||
captureException( error, {
|
||
severity: 'critical',
|
||
extra: {
|
||
componentStack,
|
||
},
|
||
} );
|
||
}
|
||
|
||
handleRefresh = () => {
|
||
window.location.reload();
|
||
};
|
||
|
||
handleOpenSupport = () => {
|
||
window.open(
|
||
'https://wordpress.org/support/plugin/woocommerce/',
|
||
'_blank'
|
||
);
|
||
};
|
||
|
||
render() {
|
||
if ( this.state.hasError ) {
|
||
return (
|
||
<div className="woocommerce-global-error-boundary">
|
||
<h1 className="woocommerce-global-error-boundary__heading">
|
||
{ __( 'Oops, something went wrong', 'woocommerce' ) }
|
||
</h1>
|
||
<p className="woocommerce-global-error-boundary__subheading">
|
||
{ __(
|
||
'We’re sorry for the inconvenience. Please try reloading the page, or you can get support from the community forums.',
|
||
'woocommerce'
|
||
) }
|
||
</p>
|
||
<div className="woocommerce-global-error-boundary__actions">
|
||
<Button
|
||
variant="secondary"
|
||
onClick={ this.handleOpenSupport }
|
||
>
|
||
{ __( 'Get Support', 'woocommerce' ) }
|
||
</Button>
|
||
<Button
|
||
variant="primary"
|
||
onClick={ this.handleRefresh }
|
||
>
|
||
{ __( 'Reload Page', 'woocommerce' ) }
|
||
</Button>
|
||
</div>
|
||
<details className="woocommerce-global-error-boundary__details">
|
||
<summary>
|
||
{ __( 'Click for error details', 'woocommerce' ) }
|
||
</summary>
|
||
<div className="woocommerce-global-error-boundary__details-content">
|
||
<strong className="woocommerce-global-error-boundary__error">
|
||
{ this.state.error &&
|
||
this.state.error.toString() }
|
||
</strong>
|
||
<p>
|
||
{ this.state.errorInfo &&
|
||
this.state.errorInfo.componentStack }
|
||
</p>
|
||
</div>
|
||
</details>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return this.props.children;
|
||
}
|
||
}
|