Skip to content

Commit

Permalink
fix(editor-preview): reset error boundaries on editor content change (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
char0n authored Aug 7, 2023
1 parent 8dfe48f commit 6fbf235
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 20 deletions.
24 changes: 4 additions & 20 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,9 @@ import EditorContentOriginPlugin from './plugins/editor-content-origin/index.js'
import EditorContentTypePlugin from './plugins/editor-content-type/index.js';
import EditorContentPersistencePlugin from './plugins/editor-content-persistence/index.js';
import EditorContentFixturesPlugin from './plugins/editor-content-fixtures/index.js';
import EditorSafeRenderPlugin from './plugins/editor-safe-render/index.js';
import SwaggerUIAdapterPlugin from './plugins/swagger-ui-adapter/index.js';

const SafeRenderPlugin = (system) =>
SwaggerUI.plugins.SafeRender({
componentList: [
'TopBar',
'SwaggerEditorLayout',
'Editor',
'EditorTextarea',
'EditorMonaco',
'EditorPane',
'EditorPaneBarTop',
'EditorPreviewPane',
'ValidationPane',
'AlertDialog',
'ConfirmDialog',
'Dropzone',
],
})(system);

const SwaggerEditor = React.memo((props) => {
const mergedProps = deepmerge(SwaggerEditor.defaultProps, props);

Expand Down Expand Up @@ -73,6 +56,7 @@ SwaggerEditor.plugins = {
EditorPreviewSwaggerUI: EditorPreviewSwaggerUIPlugin,
EditorPreviewAsyncAPI: EditorPreviewAsyncAPIPlugin,
EditorPreviewApiDesignSystems: EditorPreviewApiDesignSystemsPlugin,
EditorSafeRender: EditorSafeRenderPlugin,
TopBar: TopBarPlugin,
SplashScreenPlugin,
Layout: LayoutPlugin,
Expand All @@ -98,7 +82,7 @@ SwaggerEditor.presets = {
TopBarPlugin,
SplashScreenPlugin,
LayoutPlugin,
SafeRenderPlugin,
EditorSafeRenderPlugin,
],
monaco: () => [
ModalsPlugin,
Expand All @@ -121,7 +105,7 @@ SwaggerEditor.presets = {
TopBarPlugin,
SplashScreenPlugin,
LayoutPlugin,
SafeRenderPlugin,
EditorSafeRenderPlugin,
],
default: (...args) => SwaggerEditor.presets.monaco(...args),
};
Expand Down
74 changes: 74 additions & 0 deletions src/plugins/editor-safe-render/components/ErrorBoundary.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';

class ErrorBoundary extends Component {
static defaultState = { hasError: false, error: null, editorContent: null };

static getDerivedStateFromError(error) {
return { hasError: true, error };
}

constructor(...args) {
super(...args);
this.state = this.constructor.defaultState;
}

componentDidMount() {
const { editorSelectors } = this.props;

this.setState({ editorContent: editorSelectors.selectContent() });
}

componentDidUpdate(prevProps, prevState) {
const { editorSelectors } = this.props;
const hasEditorContentChanged = prevState.editorContent !== editorSelectors.selectContent();

if (!hasEditorContentChanged) return;

const newState = { editorContent: editorSelectors.selectContent() };

if (prevState.hasError) {
newState.hasError = false;
newState.error = null;
}

this.setState(newState);
}

componentDidCatch(error, errorInfo) {
const {
fn: { componentDidCatch },
} = this.props;

componentDidCatch(error, errorInfo);
}

render() {
const { hasError, error } = this.state;
const { getComponent, targetName, children } = this.props;

if (hasError && error) {
const FallbackComponent = getComponent('Fallback');
return <FallbackComponent name={targetName} />;
}

return children;
}
}
ErrorBoundary.propTypes = {
targetName: PropTypes.string,
getComponent: PropTypes.func.isRequired,
fn: PropTypes.shape({
componentDidCatch: PropTypes.func.isRequired,
}).isRequired,
editorSelectors: PropTypes.shape({
selectContent: PropTypes.func.isRequired,
}).isRequired,
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
};
ErrorBoundary.defaultProps = {
targetName: 'this component',
children: null,
};

export default ErrorBoundary;
39 changes: 39 additions & 0 deletions src/plugins/editor-safe-render/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import SwaggerUI from 'swagger-ui-react';

import ErrorBoundaryWrapper from './wrap-components/ErrorBoundaryWrapper.jsx';

/**
* This is special version of SwaggerUI.plugins.SafeRender.
* In editor context, we want to dismiss the error produced
* in error boundary if editor content has changed.
*/
const EditorSafeRenderPlugin = () => {
const safeRenderPlugin = () =>
SwaggerUI.plugins.SafeRender({
fullOverride: true,
componentList: [
'TopBar',
'SwaggerEditorLayout',
'Editor',
'EditorTextarea',
'EditorMonaco',
'EditorPane',
'EditorPaneBarTop',
'EditorPreviewPane',
'ValidationPane',
'AlertDialog',
'ConfirmDialog',
'Dropzone',
],
});

const safeRenderPluginOverride = () => ({
wrapComponents: {
ErrorBoundary: ErrorBoundaryWrapper,
},
});

return [safeRenderPlugin, safeRenderPluginOverride];
};

export default EditorSafeRenderPlugin;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';

import ErrorBoundary from '../components/ErrorBoundary.jsx';

const ErrorBoundaryWrapper = (Original, system) => {
const ErrorBoundaryOverride = (props) => {
const { editorSelectors } = system;

// eslint-disable-next-line react/jsx-props-no-spreading
return <ErrorBoundary {...props} editorSelectors={editorSelectors} />;
};

return ErrorBoundaryOverride;
};

export default ErrorBoundaryWrapper;

0 comments on commit 6fbf235

Please sign in to comment.