woocommerce/plugins/woocommerce-admin/client/error-boundary/index.tsx

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;
}
}