2024-06-11 08:14:44 +00:00
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
|
|
|
import { Component, ReactNode, ErrorInfo } from 'react';
|
|
|
|
import { __ } from '@wordpress/i18n';
|
|
|
|
import { Button } from '@wordpress/components';
|
2024-08-02 02:25:39 +00:00
|
|
|
import { captureException } from '@woocommerce/remote-logging';
|
2024-08-02 03:04:31 +00:00
|
|
|
import { bumpStat } from '@woocommerce/tracks';
|
2024-06-11 08:14:44 +00:00
|
|
|
/**
|
|
|
|
* 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 };
|
|
|
|
}
|
|
|
|
|
2024-08-02 02:25:39 +00:00
|
|
|
componentDidCatch( error: Error, errorInfo: ErrorInfo ) {
|
2024-06-11 08:14:44 +00:00
|
|
|
this.setState( { errorInfo } );
|
2024-08-02 02:25:39 +00:00
|
|
|
|
2024-08-02 03:04:31 +00:00
|
|
|
bumpStat( 'error', 'unhandled-js-error-during-render' );
|
|
|
|
|
2024-08-02 02:25:39 +00:00
|
|
|
// 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,
|
|
|
|
},
|
|
|
|
} );
|
2024-06-11 08:14:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
handleRefresh = () => {
|
|
|
|
window.location.reload();
|
|
|
|
};
|
|
|
|
|
|
|
|
handleOpenSupport = () => {
|
|
|
|
window.open(
|
|
|
|
'https://wordpress.org/support/plugin/woocommerce/',
|
|
|
|
'_blank'
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
render() {
|
|
|
|
if ( this.state.hasError ) {
|
|
|
|
return (
|
2024-06-17 05:05:11 +00:00
|
|
|
<div className="woocommerce-global-error-boundary">
|
|
|
|
<h1 className="woocommerce-global-error-boundary__heading">
|
2024-06-11 08:14:44 +00:00
|
|
|
{ __( 'Oops, something went wrong', 'woocommerce' ) }
|
|
|
|
</h1>
|
2024-06-17 05:05:11 +00:00
|
|
|
<p className="woocommerce-global-error-boundary__subheading">
|
2024-06-11 08:14:44 +00:00
|
|
|
{ __(
|
|
|
|
"We're sorry for the inconvenience. Please try reloading the page, or you can get support from the community forums.",
|
|
|
|
'woocommerce'
|
|
|
|
) }
|
|
|
|
</p>
|
2024-06-17 05:05:11 +00:00
|
|
|
<div className="woocommerce-global-error-boundary__actions">
|
2024-06-11 08:14:44 +00:00
|
|
|
<Button
|
|
|
|
variant="secondary"
|
|
|
|
onClick={ this.handleOpenSupport }
|
|
|
|
>
|
|
|
|
{ __( 'Get Support', 'woocommerce' ) }
|
|
|
|
</Button>
|
|
|
|
<Button
|
|
|
|
variant="primary"
|
|
|
|
onClick={ this.handleRefresh }
|
|
|
|
>
|
|
|
|
{ __( 'Reload Page', 'woocommerce' ) }
|
|
|
|
</Button>
|
|
|
|
</div>
|
2024-06-17 05:05:11 +00:00
|
|
|
<details className="woocommerce-global-error-boundary__details">
|
2024-06-11 08:14:44 +00:00
|
|
|
<summary>
|
|
|
|
{ __( 'Click for error details', 'woocommerce' ) }
|
|
|
|
</summary>
|
2024-06-17 05:05:11 +00:00
|
|
|
<div className="woocommerce-global-error-boundary__details-content">
|
|
|
|
<strong className="woocommerce-global-error-boundary__error">
|
2024-06-11 08:14:44 +00:00
|
|
|
{ this.state.error &&
|
|
|
|
this.state.error.toString() }
|
|
|
|
</strong>
|
|
|
|
<p>
|
|
|
|
{ this.state.errorInfo &&
|
|
|
|
this.state.errorInfo.componentStack }
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</details>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.props.children;
|
|
|
|
}
|
|
|
|
}
|