Skip to content

Commit

Permalink
update: add error boundary to catch error and restore state
Browse files Browse the repository at this point in the history
  • Loading branch information
VsevolodX committed Sep 9, 2023
1 parent b5fa3c1 commit 389fbf2
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 17 deletions.
38 changes: 21 additions & 17 deletions src/other/codemirror/CodeMirror.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ConsistencyChecks } from "@exabyte-io/code.js/dist/types";
import CodeMirrorBase, { BasicSetupOptions } from "@uiw/react-codemirror";
import React from "react";

import ErrorBoundary from "../../utils/ErrorBoundary";
import exaxyzLinter, { ChecksAnnotation, checksStateField } from "./utils/exaxyz_linter";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const LANGUAGES_MAP: Record<string, any> = {
Expand Down Expand Up @@ -50,7 +51,7 @@ class CodeMirror extends React.Component<CodeMirrorProps, CodeMirrorState> {
this.state = {
isLoaded: false,
isEditing: false,
extensions: this.computeExtensions(),
extensions: this.createExtensions(),
};
this.handleContentChange = this.handleContentChange.bind(this);
this.handleFocus = this.handleFocus.bind(this);
Expand All @@ -63,7 +64,7 @@ class CodeMirror extends React.Component<CodeMirrorProps, CodeMirrorState> {
*/
handleContentChange(newContent: string, viewUpdate: ViewUpdate) {
const { isLoaded, isEditing } = this.state;
const { updateContent, updateOnFirstLoad = true } = this.props;
const { updateContent, updateOnFirstLoad = true, checks } = this.props;
// kludge for the way state management is handled in web-app
// TODO: RESTORE whatever was removed here!!!!
if (!isLoaded && !updateOnFirstLoad) {
Expand All @@ -74,8 +75,9 @@ class CodeMirror extends React.Component<CodeMirrorProps, CodeMirrorState> {
// Otherwise content is being marked as edited when selecting a flavor in workflow designer!
if (isEditing && updateContent) updateContent(newContent);

const { checks } = this.props;
if (checks && checks.keys.length > 0) {
// @ts-ignore
console.log(viewUpdate.view.updateState);
viewUpdate.view.dispatch({
annotations: [ChecksAnnotation.of({ checks })],
});
Expand All @@ -101,7 +103,7 @@ class CodeMirror extends React.Component<CodeMirrorProps, CodeMirrorState> {
return LANGUAGES_MAP.fortran;
}

computeExtensions() {
createExtensions() {
const { completions, language } = this.props;
const completionExtension = autocompletion({ override: [completions] });
const languageExtensions = this.getLanguageExtensions(language);
Expand All @@ -113,19 +115,21 @@ class CodeMirror extends React.Component<CodeMirrorProps, CodeMirrorState> {
const { extensions } = this.state;

return (
<CodeMirrorBase
value={content || ""}
// @ts-ignore
onChange={(value: string, viewUpdate: ViewUpdate) => {
this.handleContentChange(value, viewUpdate);
}}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
basicSetup={options}
theme={theme || "light"}
// @ts-ignore
extensions={extensions}
/>
<ErrorBoundary restore>
<CodeMirrorBase
value={content || ""}
// @ts-ignore
onChange={(value: string, viewUpdate: ViewUpdate) => {
this.handleContentChange(value, viewUpdate);
}}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
basicSetup={options}
theme={theme || "light"}
// @ts-ignore
extensions={extensions}
/>
</ErrorBoundary>
);
}
}
Expand Down
58 changes: 58 additions & 0 deletions src/utils/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import Button from "@mui/material/Button";
import React, { ReactNode } from "react";

interface ErrorBoundaryState {
hasError: boolean;
backup: ReactNode | null;
}

interface ErrorBoundaryProps {
children: ReactNode;
restore?: boolean;
}

class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = {
hasError: false,
backup: null,
};
}

componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
console.error("Caught error:", error, errorInfo);

// eslint-disable-next-line react/destructuring-assignment
if (this.props.restore) {
this.restore();
}
}

restore = (): void => {
this.setState({ hasError: false });
};

render(): ReactNode {
const { backup, hasError } = this.state;
const { children, restore: restore1 } = this.props;
if (hasError && !restore1) {
return (
<div>
<h1>Something went wrong.</h1>
<button type="button" onClick={this.restore}>
Try to restore
</button>
</div>
);
}

if (backup === null) {
this.setState({ backup: children });
}

return children;
}
}

export default ErrorBoundary;

0 comments on commit 389fbf2

Please sign in to comment.