Skip to content

Commit

Permalink
Merge pull request #824 from nextstrain/narrative-arrows
Browse files Browse the repository at this point in the history
allow navigation in narratives via arrow keys
  • Loading branch information
jameshadfield authored Nov 15, 2019
2 parents 9590ae3 + 6b60a98 commit 0d08d63
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 9 deletions.
63 changes: 63 additions & 0 deletions docs-src/docs/narratives/create-pdf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
title: "Converting a narrative to PDF"
---

This documentation will walk through how to turn a narrative into a static PDF.
We understand that there are multiple reasons why PDFs may be a necessary method of distribution, but stress that narratives are designed to be viewed through auspice where you have the ability to interact with and interrogate the data.

If you run into any bugs, please [get in contact with us (email)](mailto:[email protected]).

## Prerequisites

This requires [Decktape](https://github.com/astefanutti/decktape) to be installed, which itself requires Node.js.
If you've followed the [auspice install instructions](../introduction/install) and are using a conda environment then this is straightforward.
If not, you can create the necessary conda environment via:

```bash
conda create --name auspice nodejs=10
```

Install Decktape via:
```bash
conda activate auspice # or whichever conda environment has nodejs
npm install --global decktape
```


## Saving an online narrative to PDF

Let's say we want to save the example Genomic-Epi Situation Report for Ebola in the DRC available at [nextstrain.org/narratives/inrb-ebola-example-sit-rep](https://nextstrain.org/narratives/inrb-ebola-example-sit-rep) which you can see has 6 different "pages" of content.
We use decktape to access these different pages & save their output as PDF via:

```bash
decktape generic --load-pause 3000 --key ArrowDown --size 1600x900 \
https://nextstrain.org/narratives/inrb-ebola-example-sit-rep example.pdf
```
You'll see a lot of bad-looking warning messages printed in the terminal 😱, but as long as you see "Printed 7 slides" or similar then the command has worked.
You should now be able to view a static PDF of the narrative, in this case at `example.pdf`.
Feel free to play around with the different options available to decktape (run `decktape generic --help` to see them).

> Note that this technique won't work if authentication is required. Please refer to the section on saving local narratives to PDF for how to do this.
## Saving a local narrative to PDF

If you have a local narrative then the concept is similar to saving an online narrative, except we need to start a auspice server locally to display our narrative & allow it to be accessed by decktape.
These instructions assume you have installed auspice into a conda enviornment as per the [auspice install instructions](../introduction/install).

Start up auspice locally, sourcing your own datasets & narratives via:
```bash
conda activate auspice # or whichever conda environment has auspice installed
auspice view --datasetDir <path-to-datasets> --narrativeDir <path-to-narratives>
```

You should now be able to view your narrative at [localhost:4000](http://localhost:4000).
For the purposes of this guide, i'll assume that we've followed the [Writing a Narrative](how-to-write) guide, and thus have a narrative describing mumps in North America available at [localhost:4000/narratives/example](http://localhost:4000/narratives/example).

In a separate terminal window, run
```bash
conda activate auspice # or whichever conda environment has dectape installed from earlier
decktape generic --load-pause 3000 --key ArrowDown --size 1600x900 \
http://localhost:4000/narratives/example example.pdf
```
You'll see a lot of bad-looking warning messages printed in the terminal 😱, but as long as you see "Printed 11 slides" or similar then the command has worked.
You should now be able to view a static PDF of the narrative, in this case at `example.pdf`.
3 changes: 1 addition & 2 deletions docs-src/docs/narratives/how-to-write.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ You can skip this step if you have generated your own dataset, but for the purpo

```bash
mkdir datasets
curl http://data.nextstrain.org/mumps_na_meta.json --compressed -o datasets/mumps_na_meta.json
curl http://data.nextstrain.org/mumps_na_tree.json --compressed -o datasets/mumps_na_tree.json
curl http://data.nextstrain.org/mumps_na.json --compressed -o datasets/mumps_na.json
```

You should now be able to visualise the dataset (without any narrative functionality) via `auspice view --datasetDir datasets`
Expand Down
3 changes: 2 additions & 1 deletion docs-src/website/sidebars.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
],
"Narratives": [
"narratives/introduction",
"narratives/how-to-write"
"narratives/how-to-write",
"narratives/create-pdf"
],
"Release Notes": [
"releases/changelog",
Expand Down
22 changes: 16 additions & 6 deletions src/components/narrative/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
import React from "react";
import { connect } from "react-redux";
import queryString from "query-string";
import Mousetrap from "mousetrap";
import { NarrativeStyles, linkStyles, OpacityFade } from './styles';
import ReactPageScroller from "./ReactPageScroller";
import { changePage } from "../../actions/navigation";
import { CHANGE_URL_QUERY_BUT_NOT_REDUX_STATE, TOGGLE_NARRATIVE } from "../../actions/types";
import { sidebarColor } from "../../globalStyles";
import { narrativeNavBarHeight } from "../../util/globals";

/* regarding refs: https://reactjs.org/docs/refs-and-the-dom.html#exposing-dom-refs-to-parent-components */
Expand Down Expand Up @@ -42,6 +42,14 @@ class Narrative extends React.Component {
}));
}
};
this.goToNextSlide = () => {
if (this.state.showingEndOfNarrativePage) return; // no-op
this.reactPageScroller.goToPage(this.props.currentInFocusBlockIdx+1);
};
this.goToPreviousSlide = () => {
if (this.props.currentInFocusBlockIdx === 0) return; // no-op
this.reactPageScroller.goToPage(this.props.currentInFocusBlockIdx-1);
};
}
componentDidMount() {
if (window.twttr && window.twttr.ready) {
Expand All @@ -52,6 +60,11 @@ class Narrative extends React.Component {
if (this.props.currentInFocusBlockIdx !== 0) {
this.reactPageScroller.goToPage(this.props.currentInFocusBlockIdx);
}
/* bind arrow keys to move around in narrative */
/* Note that "normal" page scrolling is not avaialble in narrative mode
and that scrolling the sidebar is associated with changing the narrative slide */
Mousetrap.bind(['left', 'up'], this.goToPreviousSlide);
Mousetrap.bind(['right', 'down'], this.goToNextSlide);
}
renderChevron(pointUp) {
const dims = {w: 30, h: 30};
Expand All @@ -63,15 +76,11 @@ class Narrative extends React.Component {
};
if (pointUp) style.top = narrativeNavBarHeight + progressHeight;
else style.bottom = 0;
let gotoIdx = pointUp ? this.props.currentInFocusBlockIdx-1 : this.props.currentInFocusBlockIdx+1;
if (this.state.showingEndOfNarrativePage) {
gotoIdx = this.props.blocks.length-1;
}
const svgPathD = pointUp ?
"M240.971 130.524l194.343 194.343c9.373 9.373 9.373 24.569 0 33.941l-22.667 22.667c-9.357 9.357-24.522 9.375-33.901.04L224 227.495 69.255 381.516c-9.379 9.335-24.544 9.317-33.901-.04l-22.667-22.667c-9.373-9.373-9.373-24.569 0-33.941L207.03 130.525c9.372-9.373 24.568-9.373 33.941-.001z" :
"M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z";
return (
<div id={`hand${pointUp?"Up":"Down"}`} style={style} onClick={() => this.reactPageScroller.goToPage(gotoIdx)}>
<div id={`hand${pointUp?"Up":"Down"}`} style={style} onClick={pointUp ? this.goToPreviousSlide : this.goToNextSlide}>
<svg width={`${dims.w}px`} height={`${dims.h}px`} viewBox="0 0 448 512">
<path d={svgPathD} fill="black"/>
</svg>
Expand Down Expand Up @@ -160,6 +169,7 @@ class Narrative extends React.Component {
pathname: this.props.blocks[this.props.currentInFocusBlockIdx].dataset,
query: queryString.parse(this.props.blocks[this.props.currentInFocusBlockIdx].url)
});
Mousetrap.unbind(['left', 'right', 'up', 'down']);
}
}
export default Narrative;

0 comments on commit 0d08d63

Please sign in to comment.