diff --git a/src/commons/navigationBar/subcomponents/SicpNavigationBar.tsx b/src/commons/navigationBar/subcomponents/SicpNavigationBar.tsx
index 33046fde69..6dbc46bda3 100644
--- a/src/commons/navigationBar/subcomponents/SicpNavigationBar.tsx
+++ b/src/commons/navigationBar/subcomponents/SicpNavigationBar.tsx
@@ -3,8 +3,8 @@ import { IconNames } from '@blueprintjs/icons';
import * as React from 'react';
import { useHistory, useParams } from 'react-router';
import controlButton from 'src/commons/ControlButton';
+import { getNext, getPrev } from 'src/features/sicp/TableOfContentsHelper';
-import tocNavigation from '../../../features/sicp/data/toc-navigation.json';
import { TableOfContentsButton } from '../../../features/sicp/TableOfContentsButton';
import SicpToc from '../../../pages/sicp/subcomponents/SicpToc';
@@ -16,59 +16,33 @@ const SicpNavigationBar: React.FC = () => {
const { section } = useParams<{ section: string }>();
const history = useHistory();
+ const prev = getPrev(section);
+ const next = getNext(section);
+
const handleCloseToc = () => setIsTocOpen(false);
+ const handleOpenToc = () => setIsTocOpen(true);
+ const handleNavigation = (sect: string) => {
+ history.push('/interactive-sicp/' + sect);
+ };
// Button to open table of contents
- const tocButton = React.useMemo(() => {
- const handleOpenToc = () => setIsTocOpen(true);
- return ;
- }, []);
+ const tocButton = ;
// Previous button only displayed when next page is valid.
- const prevButton = React.useMemo(() => {
- const sect = tocNavigation[section];
- if (!sect) {
- return;
- }
-
- const prev = sect['prev'];
- if (!prev) {
- return;
- }
-
- const handlePrev = () => {
- history.push('/interactive-sicp/' + prev);
- };
-
- return (
- prev &&
{controlButton('Previous', IconNames.ARROW_LEFT, handlePrev)}
- );
- }, [history, section]);
+ const prevButton = prev && (
+
+ {controlButton('Previous', IconNames.ARROW_LEFT, () => handleNavigation(prev))}
+
+ );
// Next button only displayed when next page is valid.
- const nextButton = React.useMemo(() => {
- const sect = tocNavigation[section];
- if (!sect) {
- return;
- }
-
- const next = sect['next'];
- if (!next) {
- return;
- }
-
- const handleNext = () => {
- history.push('/interactive-sicp/' + next);
- };
-
- return (
- next && (
-
- {controlButton('Next', IconNames.ARROW_RIGHT, handleNext, { iconOnRight: true })}
-
- )
- );
- }, [history, section]);
+ const nextButton = next && (
+
+ {controlButton('Next', IconNames.ARROW_RIGHT, () => handleNavigation(next), {
+ iconOnRight: true
+ })}
+
+ );
const drawerProps = {
onClose: handleCloseToc,
diff --git a/src/commons/navigationBar/subcomponents/__tests__/__snapshots__/SicpNavigationBar.tsx.snap b/src/commons/navigationBar/subcomponents/__tests__/__snapshots__/SicpNavigationBar.tsx.snap
index 2f460b98d6..4a36f17df5 100644
--- a/src/commons/navigationBar/subcomponents/__tests__/__snapshots__/SicpNavigationBar.tsx.snap
+++ b/src/commons/navigationBar/subcomponents/__tests__/__snapshots__/SicpNavigationBar.tsx.snap
@@ -8,7 +8,7 @@ exports[`Navbar renders correctly 1`] = `
-
+
Next
diff --git a/src/features/sicp/TableOfContentsHelper.ts b/src/features/sicp/TableOfContentsHelper.ts
new file mode 100644
index 0000000000..1616d6947c
--- /dev/null
+++ b/src/features/sicp/TableOfContentsHelper.ts
@@ -0,0 +1,13 @@
+import tocNavigation from './data/toc-navigation.json';
+
+export const getNext = (section: string): string | undefined => {
+ const node = tocNavigation[section];
+
+ return node && node['next'];
+};
+
+export const getPrev = (section: string): string | undefined => {
+ const node = tocNavigation[section];
+
+ return node && node['prev'];
+};
diff --git a/src/features/sicp/__tests__/TableOfContentsHelper.ts b/src/features/sicp/__tests__/TableOfContentsHelper.ts
new file mode 100644
index 0000000000..486875b24a
--- /dev/null
+++ b/src/features/sicp/__tests__/TableOfContentsHelper.ts
@@ -0,0 +1,31 @@
+import { getNext, getPrev } from '../TableOfContentsHelper';
+
+const data = {
+ '1': { next: '2' },
+ '2': { next: '3', prev: '1' },
+ '3': { prev: '2' }
+};
+
+jest.mock('../data/toc-navigation.json', () => data);
+
+describe('Table of contents helper', () => {
+ test('generate next correctly', () => {
+ expect(getNext('1')).toBe('2');
+ expect(getNext('2')).toBe('3');
+ expect(getNext('3')).toBeUndefined();
+ });
+
+ test('generate prev correctly', () => {
+ expect(getPrev('1')).toBeUndefined();
+ expect(getPrev('2')).toBe('1');
+ expect(getPrev('3')).toBe('2');
+ });
+
+ test('handle invalid values correctly', () => {
+ expect(getNext('invalid')).toBeUndefined();
+ expect(getNext('')).toBeUndefined();
+
+ expect(getPrev('invalid')).toBeUndefined();
+ expect(getPrev('')).toBeUndefined();
+ });
+});
diff --git a/src/pages/sicp/Sicp.tsx b/src/pages/sicp/Sicp.tsx
index 74f2bbc9cd..6e2f9384e7 100644
--- a/src/pages/sicp/Sicp.tsx
+++ b/src/pages/sicp/Sicp.tsx
@@ -1,14 +1,15 @@
import 'katex/dist/katex.min.css';
-import { Classes, NonIdealState, Spinner } from '@blueprintjs/core';
+import { Button, Classes, NonIdealState, Spinner } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import classNames from 'classnames';
import * as React from 'react';
import { useDispatch } from 'react-redux';
-import { RouteComponentProps, useParams } from 'react-router';
+import { RouteComponentProps, useHistory, useParams } from 'react-router';
import Constants from 'src/commons/utils/Constants';
import { resetWorkspace, toggleUsingSubst } from 'src/commons/workspace/WorkspaceActions';
import { parseArr, ParseJsonError } from 'src/features/sicp/parser/ParseJson';
+import { getNext, getPrev } from 'src/features/sicp/TableOfContentsHelper';
import SicpIndexPage from './subcomponents/SicpIndexPage';
@@ -66,6 +67,7 @@ const Sicp: React.FC = props => {
const { section } = useParams<{ section: string }>();
const topRef = React.useRef(null);
const refs = React.useRef({});
+ const history = useHistory();
// Fetch json data
React.useEffect(() => {
@@ -137,6 +139,18 @@ const Sicp: React.FC = props => {
dispatch(resetWorkspace('sicp'));
dispatch(toggleUsingSubst(false, 'sicp'));
};
+ const handleNavigation = (sect: string | undefined) => {
+ history.push('/interactive-sicp/' + sect);
+ };
+
+ const navigationButtons = (
+
+ {getPrev(section) && (
+
+ )}
+ {getNext(section) && }
+
+ );
return (
@@ -147,7 +161,10 @@ const Sicp: React.FC
= props => {
) : section === 'index' ? (
) : (
- {data}
+
+ {data}
+ {navigationButtons}
+
)}
diff --git a/src/styles/_sicp.scss b/src/styles/_sicp.scss
index 1b1557100f..93d96ded6d 100644
--- a/src/styles/_sicp.scss
+++ b/src/styles/_sicp.scss
@@ -44,6 +44,23 @@ $sicp-background-color: #ffffff;
height: fit-content;
background-color: $sicp-background-color;
+ .sicp-navigation-buttons {
+ margin: 25px 0;
+ display: flex;
+ flex-flow: row nowrap;
+ justify-content: space-between;
+
+ .bp3-button {
+ background-color: $cadet-color-3;
+ width: 80px;
+ padding: 10px 15px;
+ }
+
+ .bp3-button:hover {
+ background-color: $cadet-color-1;
+ }
+ }
+
p {
display: inline;
}