Skip to content

Commit

Permalink
Interactive Sicp Bug Fixes (#1812)
Browse files Browse the repository at this point in the history
* Adjust toc and snippet styles

* Adjust styles for non-ideal-state

* Add error boundary

* Add tests for sicp error component

* Change redirect to useHistory hook

* Tweak math font size

* Remove redundant css

* Adjust font size of code block

* Move error related files into features folder

* Fix scrolling issues

* Fix scroll bug

* Fix formatting

* Separate refs from large sections
  • Loading branch information
samuelfangjw committed Jun 21, 2021
1 parent 3245d0a commit 434513f
Show file tree
Hide file tree
Showing 9 changed files with 296 additions and 92 deletions.
36 changes: 36 additions & 0 deletions src/features/sicp/errors/SicpErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Component, ErrorInfo, ReactNode } from 'react';

import getSicpError, { SicpErrorType } from './SicpErrors';

type Props = {
children: ReactNode;
};

type State = {
hasError: boolean;
};

class SicpErrorBoundary extends Component<Props, State> {
public state: State = {
hasError: false
};

public static getDerivedStateFromError(_: Error): State {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}

public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('Uncaught error:', error, errorInfo);
}

public render() {
if (this.state.hasError) {
return getSicpError(SicpErrorType.UNEXPECTED_ERROR);
}

return this.props.children;
}
}

export default SicpErrorBoundary;
58 changes: 58 additions & 0 deletions src/features/sicp/errors/SicpErrors.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { NonIdealState } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';

export enum SicpErrorType {
UNEXPECTED_ERROR,
PAGE_NOT_FOUND_ERROR,
PARSING_ERROR
}

const unexpectedError = (
<div>
Something unexpected went wrong trying to load this page. Please try refreshing the page. If the
issue persists, kindly let us know by filing an issue at{' '}
<a href="https://github.com/source-academy/cadet-frontend">
https://github.com/source-academy/cadet-frontend
</a>
.
</div>
);

const pageNotFoundError = (
<div>
We could not find the page you were looking for. Please check the URL again. If you believe the
URL is correct, kindly let us know by filing an issue at{' '}
<a href="https://github.com/source-academy/cadet-frontend">
https://github.com/source-academy/cadet-frontend
</a>
.
</div>
);

const parsingError = (
<div>
An error occured while loading the page. Kindly let us know by filing an issue at{' '}
<a href="https://github.com/source-academy/cadet-frontend">
https://github.com/source-academy/cadet-frontend
</a>{' '}
and we will get it fixed as soon as possible.
</div>
);

const errorComponent = (description: JSX.Element) => (
<NonIdealState title="Something went wrong :(" description={description} icon={IconNames.ERROR} />
);

const getSicpError = (type: SicpErrorType) => {
switch (type) {
case SicpErrorType.PAGE_NOT_FOUND_ERROR:
return errorComponent(pageNotFoundError);
case SicpErrorType.PARSING_ERROR:
return errorComponent(parsingError);
default:
// handle unexpected error case
return errorComponent(unexpectedError);
}
};

export default getSicpError;
20 changes: 20 additions & 0 deletions src/features/sicp/errors/__tests__/SicpErrors.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { mount } from 'enzyme';

import getSicpError, { SicpErrorType } from '../SicpErrors';

describe('Sicp errors:', () => {
test('unexpected error renders correctly', () => {
const tree = mount(getSicpError(SicpErrorType.UNEXPECTED_ERROR));
expect(tree.debug()).toMatchSnapshot();
});

test('page not found error renders correctly', () => {
const tree = mount(getSicpError(SicpErrorType.PAGE_NOT_FOUND_ERROR));
expect(tree.debug()).toMatchSnapshot();
});

test('unexpected error renders correctly', () => {
const tree = mount(getSicpError(SicpErrorType.PARSING_ERROR));
expect(tree.debug()).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Sicp errors: page not found error renders correctly 1`] = `
"<Blueprint3.NonIdealState title=\\"Something went wrong :(\\" description={{...}} icon=\\"error\\">
<div className=\\"bp3-non-ideal-state\\">
<div className=\\"bp3-non-ideal-state-visual\\">
<Blueprint3.Icon icon=\\"error\\" iconSize={60}>
<span icon=\\"error\\" className=\\"bp3-icon bp3-icon-error\\" title={[undefined]}>
<svg fill={[undefined]} data-icon=\\"error\\" width={60} height={60} viewBox=\\"0 0 20 20\\">
<desc>
error
</desc>
<path d=\\"M10 0C4.48 0 0 4.48 0 10s4.48 10 10 10 10-4.48 10-10S15.52 0 10 0zm1 16H9v-2h2v2zm0-3H9V4h2v9z\\" fillRule=\\"evenodd\\" />
</svg>
</span>
</Blueprint3.Icon>
</div>
<Component>
<h4 className=\\"bp3-heading\\">
Something went wrong :(
</h4>
</Component>
<div>
We could not find the page you were looking for. Please check the URL again. If you believe the URL is correct, kindly let us know by filing an issue at
<a href=\\"https://github.com/source-academy/cadet-frontend\\">
https://github.com/source-academy/cadet-frontend
</a>
.
</div>
</div>
</Blueprint3.NonIdealState>"
`;
exports[`Sicp errors: unexpected error renders correctly 1`] = `
"<Blueprint3.NonIdealState title=\\"Something went wrong :(\\" description={{...}} icon=\\"error\\">
<div className=\\"bp3-non-ideal-state\\">
<div className=\\"bp3-non-ideal-state-visual\\">
<Blueprint3.Icon icon=\\"error\\" iconSize={60}>
<span icon=\\"error\\" className=\\"bp3-icon bp3-icon-error\\" title={[undefined]}>
<svg fill={[undefined]} data-icon=\\"error\\" width={60} height={60} viewBox=\\"0 0 20 20\\">
<desc>
error
</desc>
<path d=\\"M10 0C4.48 0 0 4.48 0 10s4.48 10 10 10 10-4.48 10-10S15.52 0 10 0zm1 16H9v-2h2v2zm0-3H9V4h2v9z\\" fillRule=\\"evenodd\\" />
</svg>
</span>
</Blueprint3.Icon>
</div>
<Component>
<h4 className=\\"bp3-heading\\">
Something went wrong :(
</h4>
</Component>
<div>
Something unexpected went wrong trying to load this page. Please try refreshing the page. If the issue persists, kindly let us know by filing an issue at
<a href=\\"https://github.com/source-academy/cadet-frontend\\">
https://github.com/source-academy/cadet-frontend
</a>
.
</div>
</div>
</Blueprint3.NonIdealState>"
`;
exports[`Sicp errors: unexpected error renders correctly 2`] = `
"<Blueprint3.NonIdealState title=\\"Something went wrong :(\\" description={{...}} icon=\\"error\\">
<div className=\\"bp3-non-ideal-state\\">
<div className=\\"bp3-non-ideal-state-visual\\">
<Blueprint3.Icon icon=\\"error\\" iconSize={60}>
<span icon=\\"error\\" className=\\"bp3-icon bp3-icon-error\\" title={[undefined]}>
<svg fill={[undefined]} data-icon=\\"error\\" width={60} height={60} viewBox=\\"0 0 20 20\\">
<desc>
error
</desc>
<path d=\\"M10 0C4.48 0 0 4.48 0 10s4.48 10 10 10 10-4.48 10-10S15.52 0 10 0zm1 16H9v-2h2v2zm0-3H9V4h2v9z\\" fillRule=\\"evenodd\\" />
</svg>
</span>
</Blueprint3.Icon>
</div>
<Component>
<h4 className=\\"bp3-heading\\">
Something went wrong :(
</h4>
</Component>
<div>
An error occured while loading the page. Kindly let us know by filing an issue at
<a href=\\"https://github.com/source-academy/cadet-frontend\\">
https://github.com/source-academy/cadet-frontend
</a>
and we will get it fixed as soon as possible.
</div>
</div>
</Blueprint3.NonIdealState>"
`;
12 changes: 8 additions & 4 deletions src/features/sicp/parser/ParseJson.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ const handleFootnote = (obj: JsonType, refs: React.MutableRefObject<{}>) => {
return (
<div>
{obj['count'] === 1 && <hr />}
<div ref={ref => (refs.current[obj['id']!] = ref)} className="sicp-footnote">
<div className="sicp-footnote">
<div ref={ref => (refs.current[obj['id']!] = ref)} />
<a href={obj['href']}>{'[' + obj['count'] + '] '}</a>
{parseArr(obj['child']!, refs)}
</div>
Expand Down Expand Up @@ -113,7 +114,8 @@ const handleSnippet = (obj: JsonType) => {
};

const handleFigure = (obj: JsonType, refs: React.MutableRefObject<{}>) => (
<div ref={ref => (refs.current[obj['id']!] = ref)} className="sicp-figure">
<div className="sicp-figure">
<div ref={ref => (refs.current[obj['id']!] = ref)} />
{handleImage(obj, refs)}
{obj['captionName'] && (
<h5 className="sicp-caption">
Expand Down Expand Up @@ -152,7 +154,8 @@ const handleTD = (obj: JsonType, refs: React.MutableRefObject<{}>, index: intege

const handleExercise = (obj: JsonType, refs: React.MutableRefObject<{}>) => {
return (
<div ref={ref => (refs.current[obj['id']!] = ref)}>
<div>
<div ref={ref => (refs.current[obj['id']!] = ref)} />
<SicpExercise
title={obj['title']!}
body={parseArr(obj['child']!, refs)}
Expand Down Expand Up @@ -253,7 +256,8 @@ export const processingFunctions = {

TEXT: (obj: JsonType, refs: React.MutableRefObject<{}>) => (
<>
<div ref={ref => (refs.current[obj['id']!] = ref)} className="sicp-text">
<div className="sicp-text">
<div ref={ref => (refs.current[obj['id']!] = ref)} />
{parseArr(obj['child']!, refs)}
</div>
<br />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,18 +115,21 @@ exports[`Parse epigraph EPIGRAPH with title successful 1`] = `
exports[`Parse exercise EXERCISE with solution successful 1`] = `
"<div>
<div />
<SicpExercise title={[undefined]} body={{...}} solution={{...}} />
</div>"
`;
exports[`Parse exercise EXERCISE without solution successful 1`] = `
"<div>
<div />
<SicpExercise title=\\"Title\\" body={{...}} solution={[undefined]} />
</div>"
`;

exports[`Parse figures FIGURE with image and scale successful 1`] = `
"<div className=\\"sicp-figure\\">
<div />
<img src=\\"https://source-academy.github.io/sicp/sicp.png\\" alt=\\"id\\" width=\\"50%\\" />
<h5 className=\\"sicp-caption\\">
name
Expand All @@ -141,6 +144,7 @@ exports[`Parse figures FIGURE with image and scale successful 1`] = `
exports[`Parse figures FIGURE with image successful 1`] = `
"<div className=\\"sicp-figure\\">
<div />
<img src=\\"https://source-academy.github.io/sicp/sicp.png\\" alt=\\"id\\" width=\\"100%\\" />
<h5 className=\\"sicp-caption\\">
name
Expand All @@ -155,6 +159,7 @@ exports[`Parse figures FIGURE with image successful 1`] = `
exports[`Parse figures FIGURE with snippet successful 1`] = `
"<div className=\\"sicp-figure\\">
<div />
<Component body=\\"1 + 1;\\" id={[undefined]} initialEditorValueHash=\\"IwAg1CwNxA\\" initialFullProgramHash={[undefined]} initialPrependHash={[undefined]} output={[undefined]} />
<h5 className=\\"sicp-caption\\">
name
Expand All @@ -169,6 +174,7 @@ exports[`Parse figures FIGURE with snippet successful 1`] = `
exports[`Parse figures FIGURE with table successful 1`] = `
"<div className=\\"sicp-figure\\">
<div />
<table>
<tbody>
<tr>
Expand Down Expand Up @@ -220,6 +226,7 @@ exports[`Parse footnote DISPLAYFOOTNOTE count is 1 successful 1`] = `
"<div>
<hr />
<div className=\\"sicp-footnote\\">
<div />
<a href=\\"\\">
[1]
</a>
Expand All @@ -235,6 +242,7 @@ exports[`Parse footnote DISPLAYFOOTNOTE count is 1 successful 1`] = `
exports[`Parse footnote DISPLAYFOOTNOTE count is 2 successful 1`] = `
"<div>
<div className=\\"sicp-footnote\\">
<div />
<a href=\\"\\">
[2]
</a>
Expand Down Expand Up @@ -364,6 +372,7 @@ exports[`Parse section SECTION successful 1`] = `
<div>
<span>
<div className=\\"sicp-text\\">
<div />
<span>
<p>
Mock Text
Expand All @@ -379,6 +388,7 @@ exports[`Parse section SECTION successful 1`] = `
</span>
<span>
<div className=\\"sicp-text\\">
<div />
<span>
<p>
Mock Text
Expand Down
Loading

0 comments on commit 434513f

Please sign in to comment.