Skip to content

Commit

Permalink
fix: error boundary handling can now be handled by Plugin component
Browse files Browse the repository at this point in the history
  • Loading branch information
jsnwesson committed Sep 19, 2023
1 parent b6352e5 commit 0607bec
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 18 deletions.
11 changes: 6 additions & 5 deletions example-plugin-app/src/PluginOne.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React from 'react';
import { Plugin } from '@edx/frontend-platform/plugins';

function Greeting({subject}) {
return <div>Hello {subject.toUpperCase()}</div>
}

export default function PluginOne() {
return (
<Plugin className="bg-light" ready>
<section className="bg-light p-3">
<h2>Site Maintenance</h2>
<p>The site will be going down for maintenance soon.</p>
</section>
<Plugin className="bg-light">
<Greeting />
</Plugin>
);
}
28 changes: 20 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"lodash.merge": "4.6.2",
"lodash.snakecase": "4.1.1",
"pubsub-js": "1.9.4",
"react-error-boundary": "^4.0.11",
"react-intl": "^5.25.0",
"universal-cookie": "4.0.4"
},
Expand Down
40 changes: 39 additions & 1 deletion src/plugins/Plugin.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
'use client';

import React, { useEffect, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import { ErrorBoundary } from 'react-error-boundary';
import {
dispatchMountedEvent, dispatchReadyEvent, dispatchUnmountedEvent, useHostEvent,
} from './data/hooks';
import { PLUGIN_RESIZE } from './data/constants';

function ErrorFallback(error) {
// we can customize the UI as we want
return (
<div>
<h2>
Oops! An error occurred
<br />
<br />
{error.message}
</h2>
{/* Additional custom error handling */}
</div>
);
}

export default function Plugin({
children, className, style, ready,
}) {
Expand All @@ -13,6 +31,20 @@ export default function Plugin({
height: null,
});

const [errorMessage, setErrorMessage] = useState('');
const handleResetError = () => {
console.log('Error boundary reset');
setErrorMessage('');
// additional logic to perform code cleanup and state update actions
};

// Error logging function
function logErrorToService(error) {
// Use your preferred error logging service
setErrorMessage(error);
console.error('Caught an error:', errorMessage, error);
}

const finalStyle = useMemo(() => ({
...dimensions,
...style,
Expand Down Expand Up @@ -41,7 +73,13 @@ export default function Plugin({

return (
<div className={className} style={finalStyle}>
{children}
<ErrorBoundary
FallbackComponent={ErrorFallback}
onError={() => logErrorToService()}
onReset={handleResetError}
>
{children}
</ErrorBoundary>
</div>
);
}
Expand Down
24 changes: 24 additions & 0 deletions src/plugins/Plugin.test.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { render } from '@testing-library/react';
import { fireEvent } from '@testing-library/dom';
import '@testing-library/jest-dom';

import PluginContainer from './PluginContainer';
import Plugin from './Plugin';
import {
IFRAME_PLUGIN, PLUGIN_MOUNTED, PLUGIN_READY, PLUGIN_RESIZE,
} from './data/constants';
Expand Down Expand Up @@ -32,6 +34,10 @@ describe('PluginContainer', () => {
expect(container.firstChild).toBeNull();
});

it('should render the desired fallback when the iframe fails to render', () => {

});

it('should render a PluginIFrame when given an iFrame config', async () => {
const title = 'test plugin';
const component = (
Expand Down Expand Up @@ -90,3 +96,21 @@ describe('PluginContainer', () => {
expect(iframeElement.attributes.getNamedItem('class').value).toEqual('border border-0');
});
});

describe('Plugin', () => {
const breakingArray = null;
const failingMap = () => breakingArray.map(a => a);
it('should render the desired fallback when the error boundary receives a React error', () => {
const component = (
<ErrorBoundary fallback={<div>Something went wrong</div>}>
<Plugin className="bg-light" ready>
{ failingMap }
</Plugin>
</ErrorBoundary>
);

const { container } = render(component);
console.log(container.children);
expect(container.firstChild).toHaveTextContent('Something went wrong');
});
});
9 changes: 5 additions & 4 deletions src/plugins/PluginContainer.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
'use client';

import React from 'react';

// eslint-disable-next-line import/no-extraneous-dependencies
import PluginContainerIframe from './PluginContainerIframe';
import PluginErrorBoundary from './PluginErrorBoundary';

import {
IFRAME_PLUGIN,
Expand All @@ -13,6 +15,7 @@ export default function PluginContainer({ config, ...props }) {
return null;
}

// this will allow for future plugin types to be inserted in the PluginErrorBoundary
let renderer = null;
switch (config.type) {
case IFRAME_PLUGIN:
Expand All @@ -25,9 +28,7 @@ export default function PluginContainer({ config, ...props }) {
}

return (
<PluginErrorBoundary>
{renderer}
</PluginErrorBoundary>
renderer
);
}

Expand Down

0 comments on commit 0607bec

Please sign in to comment.