Skip to content

Commit

Permalink
Interactive sicp: add navigation buttons to bottom of page (#1815)
Browse files Browse the repository at this point in the history
* Add buttons to sicp pages

* Refactor navbar navigation buttons

* Add navigation functionality to buttons

* Add tests for table of contents helper

* Update snapshots
  • Loading branch information
samuelfangjw committed Jun 21, 2021
1 parent a3f10aa commit 3245d0a
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 51 deletions.
68 changes: 21 additions & 47 deletions src/commons/navigationBar/subcomponents/SicpNavigationBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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 <TableOfContentsButton key="toc" handleOpenToc={handleOpenToc} />;
}, []);
const tocButton = <TableOfContentsButton key="toc" handleOpenToc={handleOpenToc} />;

// 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 && <div key="prev">{controlButton('Previous', IconNames.ARROW_LEFT, handlePrev)}</div>
);
}, [history, section]);
const prevButton = prev && (
<div key="prev">
{controlButton('Previous', IconNames.ARROW_LEFT, () => handleNavigation(prev))}
</div>
);

// 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 && (
<div key="next">
{controlButton('Next', IconNames.ARROW_RIGHT, handleNext, { iconOnRight: true })}
</div>
)
);
}, [history, section]);
const nextButton = next && (
<div key="next">
{controlButton('Next', IconNames.ARROW_RIGHT, () => handleNavigation(next), {
iconOnRight: true
})}
</div>
);

const drawerProps = {
onClose: handleCloseToc,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ exports[`Navbar renders correctly 1`] = `
</Blueprint3.NavbarGroup>
<Blueprint3.NavbarGroup align=\\"right\\">
<div>
<Blueprint3.Button disabled={false} fill={false} intent=\\"none\\" minimal={true} className=\\"\\" type={[undefined]} rightIcon={{...}} onClick={[Function: handleNext]}>
<Blueprint3.Button disabled={false} fill={false} intent=\\"none\\" minimal={true} className=\\"\\" type={[undefined]} rightIcon={{...}} onClick={[Function (anonymous)]}>
Next
</Blueprint3.Button>
</div>
Expand Down
13 changes: 13 additions & 0 deletions src/features/sicp/TableOfContentsHelper.ts
Original file line number Diff line number Diff line change
@@ -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'];
};
31 changes: 31 additions & 0 deletions src/features/sicp/__tests__/TableOfContentsHelper.ts
Original file line number Diff line number Diff line change
@@ -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();
});
});
23 changes: 20 additions & 3 deletions src/pages/sicp/Sicp.tsx
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -66,6 +67,7 @@ const Sicp: React.FC<SicpProps> = props => {
const { section } = useParams<{ section: string }>();
const topRef = React.useRef<HTMLDivElement>(null);
const refs = React.useRef({});
const history = useHistory();

// Fetch json data
React.useEffect(() => {
Expand Down Expand Up @@ -137,6 +139,18 @@ const Sicp: React.FC<SicpProps> = props => {
dispatch(resetWorkspace('sicp'));
dispatch(toggleUsingSubst(false, 'sicp'));
};
const handleNavigation = (sect: string | undefined) => {
history.push('/interactive-sicp/' + sect);
};

const navigationButtons = (
<div className="sicp-navigation-buttons">
{getPrev(section) && (
<Button onClick={() => handleNavigation(getPrev(section))}>Previous</Button>
)}
{getNext(section) && <Button onClick={() => handleNavigation(getNext(section))}>Next</Button>}
</div>
);

return (
<div className={classNames('Sicp', Classes.RUNNING_TEXT, Classes.TEXT_LARGE, Classes.DARK)}>
Expand All @@ -147,7 +161,10 @@ const Sicp: React.FC<SicpProps> = props => {
) : section === 'index' ? (
<SicpIndexPage />
) : (
<div className="sicp-content">{data}</div>
<div className="sicp-content">
{data}
{navigationButtons}
</div>
)}
</CodeSnippetContext.Provider>
</div>
Expand Down
17 changes: 17 additions & 0 deletions src/styles/_sicp.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down

0 comments on commit 3245d0a

Please sign in to comment.