Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error boundaries for panels #1897

Merged
merged 1 commit into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions src/components/errorBoundaries/panelErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React from "react";
import styled from "styled-components";

const Background = styled.div`
background: repeating-linear-gradient(
45deg,
transparent,
transparent 20px,
#f4d2ff 20px,
#f4d2ff 21px
);
margin: 10px 10px;
padding: 10px 10px;
border: 1px solid #f4d2ff;
display: inline-block;
position: relative;
`;

interface Props {
width: number;
height: number;
name: string;
}
interface State {
hasError: boolean;
errorMessage: string;
}

class PanelErrorBoundary extends React.Component<Props, State> {
constructor(props) {
super(props);
this.state = { hasError: false, errorMessage: ''};
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return {
hasError: true,
errorMessage: error instanceof Error ? error.message : "Unknown error (thrown value was not an instance of Error)",
};
}
override componentDidCatch(error, info) {
// You can also log the error to an error reporting service
console.error(error);
console.error(info);
}

override render() {
if (!this.state.hasError) return this.props.children;

/**
* We could add something like
* <button onClick={() => {this.setState({hasError: false})}}>You can try clicking here to re-render the panel</button>
* but it risks rendering an incorrect state so I'd prefer not to
*/

return (
<Background style={{width: this.props.width, height: this.props.height}}>
<h1 style={{fontSize: '3rem'}}>{`Error! Something's gone wrong within the ${this.props.name} panel`}</h1>

<p style={{fontSize: '1.8rem'}}>
{`Error message: "${this.state.errorMessage}"`}
</p>

<p style={{fontSize: '1.8rem'}}>
{'Please consider making a bug report either on '}
<a href="https://github.com/nextstrain/auspice/issues/new" target="_blank" rel="noreferrer noopener">GitHub</a>
{' or via '}
<a href="mailto:[email protected]" target="_blank" rel="noreferrer noopener">email</a>.
(The more information you can include the better - things such as steps to reproduce the bug, your browser version, the version of Auspice etc are incredibly helpful.)
</p>
<p style={{fontSize: '1.8rem'}}>
In the meantime you could try refreshing the page which may fix things in the short term.
</p>

</Background>
);
}
}

export default PanelErrorBoundary;
51 changes: 37 additions & 14 deletions src/components/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import ErrorBoundary from "../../util/errorBoundary";
import Spinner, { PanelSpinner } from "../framework/spinner";
import MainDisplayMarkdown from "../narrative/MainDisplayMarkdown";
import MobileNarrativeDisplay from "../narrative/MobileNarrativeDisplay";
import PanelErrorBoundary from "../errorBoundaries/panelErrorBoundary";

const Entropy = lazy(() => import("../entropy"));
const Frequencies = lazy(() => import("../frequencies"));
Expand Down Expand Up @@ -155,11 +156,17 @@ class Main extends React.Component {
}
{this.props.displayNarrative || this.props.showOnlyPanels ? null : <Info width={calcUsableWidth(availableWidth, 1)} />}
{this.props.panelsToDisplay.includes("tree") ?
<Tree
<PanelErrorBoundary
width={this.inGrid() ? grid.width : full.width}
height={this.inGrid() ? grid.height : full.height}
key={keyName}
/> :
name="tree"
>
<Tree
width={this.inGrid() ? grid.width : full.width}
height={this.inGrid() ? grid.height : full.height}
key={keyName}
/>
</PanelErrorBoundary> :
null
}
{this.props.panelsToDisplay.includes("measurements") ?
Expand All @@ -172,34 +179,50 @@ class Main extends React.Component {
/>
}
>
<Measurements
<PanelErrorBoundary
width={this.inGrid() ? grid.width : full.width}
height={this.inGrid() ? grid.height : full.height}
key={keyName+"_measurements"}
showLegend={this.shouldShowMeasurementsLegend()}
/>
name="measurements"
>
<Measurements
width={this.inGrid() ? grid.width : full.width}
height={this.inGrid() ? grid.height : full.height}
key={keyName+"_measurements"}
showLegend={this.shouldShowMeasurementsLegend()}
/>
</PanelErrorBoundary>
</Suspense> :
null
}
{this.props.panelsToDisplay.includes("map") ?
<Map
<PanelErrorBoundary
width={this.shouldMapBeInGrid() ? grid.width : full.width}
height={this.shouldMapBeInGrid() ? grid.height : full.height}
key={keyName+"_map"}
justGotNewDatasetRenderNewMap={false}
legend={this.shouldShowMapLegend()}
/> :
name="map"
>
<Map
width={this.shouldMapBeInGrid() ? grid.width : full.width}
height={this.shouldMapBeInGrid() ? grid.height : full.height}
key={keyName+"_map"}
justGotNewDatasetRenderNewMap={false}
legend={this.shouldShowMapLegend()}
/>
</PanelErrorBoundary> :
null
}
{this.props.panelsToDisplay.includes("entropy") ?
(<Suspense fallback={null}>
<Entropy width={chartEntropy.width} height={chartEntropy.height} key={keyName+"_entropy"}/>
<PanelErrorBoundary width={chartEntropy.width} height={chartEntropy.height} name="entropy">
<Entropy width={chartEntropy.width} height={chartEntropy.height} key={keyName+"_entropy"}/>
</PanelErrorBoundary>
</Suspense>) :
null
}
{this.props.panelsToDisplay.includes("frequencies") && this.props.frequenciesLoaded ?
(<Suspense fallback={null}>
<Frequencies width={chartFrequencies.width} height={chartFrequencies.height} key={keyName+"_frequencies"}/>
<PanelErrorBoundary width={chartFrequencies.width} height={chartFrequencies.height} name="frequencies">
<Frequencies width={chartFrequencies.width} height={chartFrequencies.height} key={keyName+"_frequencies"}/>
</PanelErrorBoundary>
</Suspense>) :
null
}
Expand Down
Loading