@nypl/web-reader 4.1.0 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,15 +1,20 @@
1
1
  # NYPL Web Reader
2
2
 
3
- This project is a web reader built by NYPL for reading eBooks. It is built using the [Readium Architecture](https://github.com/readium/architecture), and specifically built for Webpubs. Webpub is a spec [defined by the Readium Foundation](https://github.com/readium/webpub-manifest) to provide a common abstraction between many types of web publications. Initially, this project will focus on HTML-based Webpubs and Webpubs that define PDF collections. An HTML-based Webpub can be generated from many types of eBooks, but most commonly ePubs.
3
+ This project is a web reader built by NYPL for reading eBooks. It was designed using the [Readium Architecture](https://github.com/readium/architecture), and specifically built for Webpubs. Webpub is a spec [defined by the Readium Foundation](https://github.com/readium/webpub-manifest) to provide a common abstraction between many types of web publications. This project currently focuses on HTML-based Webpubs and Webpubs that define PDF collections. An HTML-based Webpub can be generated from many types of eBooks, but most commonly ePubs.
4
4
 
5
- The project is built with [esbuild](https://esbuild.github.io/). It uses Typescript, React, Jest and Cypress, and features both a Storybook development environment and an example application under `/example`. The example is deployed here: https://nypl-web-reader.vercel.app.
5
+ ## Demo - Example App
6
6
 
7
- A big thanks to [R2D2BC](https://github.com/d-i-t-a/R2D2BC) for providing the underlying HTML navigator capabilities.
7
+ This project features an example application under `/example`, which is deployed here: https://nypl-web-reader.vercel.app.
8
8
 
9
- ## Demo
9
+ ## Built With
10
10
 
11
- - [Example reader app](https://nypl-web-reader.vercel.app)
12
- - [Storybook deployment](https://web-reader-storybook.vercel.app)
11
+ - [esbuild](https://esbuild.github.io/) - JavaScript bundler for the web-reader package
12
+ - [react-pdf](https://react-pdf.org/) - For creating PDF files on the browser and server
13
+ - [chakra-ui](https://chakra-ui.com/) - For styling the default UI components
14
+ - [TypeScript](https://www.npmjs.com/package/typescript) - JavaScript with syntax for types
15
+ - [React](https://www.npmjs.com/package/react) - For creating user interface components
16
+ - [Jest](https://jestjs.io/) & [React Testing Library](https://testing-library.com/) - For writing unit tests
17
+ - The Example App is packaged with [Parcel](https://parceljs.org/) & deployed with [Vercel](https://vercel.com/). Integration tests are run with [Cypress](https://www.cypress.io/).
13
18
 
14
19
  ## Features
15
20
 
@@ -23,17 +28,17 @@ A big thanks to [R2D2BC](https://github.com/d-i-t-a/R2D2BC) for providing the un
23
28
  - [x] Fullscreen
24
29
  - [x] Paginated / Scrolling mode toggle
25
30
  - [x] Zoom (PDF only)
26
- - [x] Offline support (prefetch and cache desired content via Service Worker, along with host app shell.
27
- - [ ] Saving bookmarks / highlights
28
31
  - [x] WAI-ARIA compliant accessibility
29
32
  - [x] Integration tested
33
+ - [ ] Offline support (prefetch and cache desired content via Service Worker, along with host app shell)
34
+ - [ ] Saving bookmarks / highlights
30
35
 
31
- ## Example
36
+ ## Examples
32
37
 
33
38
  Basic usage within a React app, using the default UI:
34
39
 
35
40
  ```typescript
36
- import WebReader from 'nypl/web-reader';
41
+ import WebReader from '@nypl/web-reader';
37
42
 
38
43
  const ReaderPage = ({ manifestUrl }) => {
39
44
  return (
@@ -45,96 +50,32 @@ const ReaderPage = ({ manifestUrl }) => {
45
50
  };
46
51
  ```
47
52
 
48
- Passing a content decryptor to the reader for use by the Client. This would be how we render AxisNow content for example:
49
-
50
- ```typescript
51
- import WebReader from "nypl/web-reader"
52
- import AxisNowDecryptor from "nypl/axisnow-access-control-web"
53
-
54
- const ReaderPage = ({manifestUrl}) => {
55
- const decryptor = new AxisNowDecryptor(...);
56
- return (
57
- <WebReader
58
- getContent={decryptor.getContent}
59
- manifestUrl={manifestUrl}
60
- />
61
- )
62
- }
63
- ```
64
-
65
- To support customization, you can piece together your own UI and call the `useWebReader` hook to get access to the reader API.
53
+ - [More React examples](/example/index.tsx) - Includes examples that render EPUB2 & EPUB3 based webpubs, remote hosted webpubs, and PDFs
54
+ - [Encrypted example](/example/axisnow-encrypted.tsx) - How to pass an content decryptor to the web-reader to render encrypted content
55
+ - [usePDFReader hook example](/example/use-pdf-reader.tsx) - Useful for instances when you know you're only going to be using the web-reader to open PDFs
56
+ - [useHTMLReader hook example](/example/use-html-reader.tsx) - Useful for cases when you know you're only going to be using the web-reader to read EPUBs
57
+ - [Real-world example: Open eBooks Web](https://github.com/NYPL/ereading-clients/blob/staging/apps/oew/src/components/theme-ui/WebReader.tsx) - NYPL application for children to read books on the web. This demonstrates how encrypted AxisNow content is passed to the web-reader.
58
+ - [Real-world example: Digital Research Books (DRB)](https://github.com/NYPL/sfr-bookfinder-front-end/blob/development/src/components/ReaderLayout/ReaderLayout.tsx) - NYPL application that collects digital versions of research books into one convenient place to search.
66
59
 
67
- ```typescript
68
- import { useWebReader, ReaderNav, ReaderFooter } from 'nypl/web-reader';
60
+ ## Required Fonts
69
61
 
70
- const CustomizedReaderPage = ({ webpubManifestUrl }) => {
71
- // takes a manifest, instantiates a Navigator, and
72
- // returns the Navigator, interaction handlers, and
73
- // the current state of the reader as an object
74
- const reader = useWebReader({
75
- webpubManifestUrl,
76
- });
62
+ In order for the Settings panel to be displayed as intended, the fonts Roboto, Georgia, and OpenDyslexic must be available to your application. Georgia is web safe, meaning it is installed by default on most devices, but the others are not. One way to include them is to copy them from the `fonts` folder in `@nypl/web-reader/example/static` into your `/public` directory. Alternatively, for Roboto, you can embed the Google Font into the <head> of your html (directions [here](https://fonts.google.com/specimen/Roboto)).
77
63
 
78
- return (
79
- <div>
80
- {/* eg. keep default header, but change its background */}
81
- <ReaderNav {...reader} className="bg-blue" />
64
+ ## Required CSS Injectables for the HTML Reader (EPUBs)
82
65
 
83
- {/* we can add custom prev/next page buttons */}
84
- <button onClick={reader.handleNextPage}>Next</button>
85
- <button onClick={reader.handlePrevPage}>Prev</button>
66
+ **Note:** This section does not apply to PDFs
86
67
 
87
- {/* you will receive content from the reader to render wherever you want */}
88
- {reader.content}
68
+ The HTML Reader can inject `<style>` tags (and other tags) into the reader iframe itself, called an "injectable". This is used to add styles to the html content of the publication.
89
69
 
90
- {/* use the default footer */}
91
- <ReaderFooter {...reader} />
92
- </div>
93
- );
94
- };
95
- ```
70
+ In order for the web-reader to render EPUBs correctly and display the OpenDyslexic font, there are some files that you must import into your application and inject into the `<WebReader />`. Those files are as follows:
96
71
 
97
- If you know you are only going to be using one type of reader, you can also call the hook just for that reader:
72
+ 1. The `opendyslexic` font
73
+ 2. The three default Readium stylesheets
98
74
 
99
- ```typescript
100
- import { usePdfReader } from 'nypl/web-reader';
75
+ We export the Readium CSS stylesheets compiled under `@nypl/web-reader/dist/injectable-html-styles/*.css`. These css files can then be imported via webpack as a url to a static file that is copied to the dist folder. You can then use this url in your injectable config.
101
76
 
102
- const MyPdfReader = ({ webpubManifestUrl, manifest }) => {
103
- const reader = usePdfReader({ manifest, webpubManifestUrl });
104
-
105
- return <div>{reader.content}</div>;
106
- };
107
- ```
108
-
109
- Finally, to use in a vanilla Javascript app:
110
-
111
- ```html
112
- <div id="web-reader" />
113
- <script>
114
- const readerDiv = document.getElementById('web-reader');
115
- renderReader(readerDiv, {
116
- manifestUrl: xxx,
117
- });
118
- </script>
119
- ```
120
-
121
- ## Styling
122
-
123
- Most styling is included in the basic UI, but we also ship a few css files that must be included:
124
-
125
- 1. Both the HTML and the PDF side have css that is necessary to be included for the dependencies we use to render correctly. This is built automatically into `@nypl/web-reader/dist/esm/index.css` and `@nypl/web-reader/dist/cjs/index.css`. Depending which package you are using, you should include one of those files in your bundle.
126
- 1. The HTML Reader can inject `<style>` tags (and other tags) into the reader iframe itself, called an "injectable". This is used to add styles to the html content of the publication. More on this is below.
127
-
128
- In order for the Settings panel to be displayed as intended, the fonts Roboto, Georgia, Helvetica, and OpenDyslexic must be available to your application. Georgia is web safe, meaning it is installed by default on most devices, but the others are not. One way to include them is to copy the `fonts` folder and its contents from `@nypl/web-reader/example/static` into your `/public` directory.
129
-
130
- ## Injectables
131
-
132
- The HTML Reader has the ability to inject custom elements into the reader iframe. This is most useful for passing stylesheets and fonts, but other elements can be injected too. It is recommended to pass the `opendyslexic` font and the default Readium stylesheets as injectables to the iframe.
133
-
134
- In the below example, we show two different ways to do this.
135
-
136
- 1. We export the Readium CSS stylesheets compiled under `@nypl/web-reader/dist/injectable-html-styles/*.css`. These css files can then be imported via webpack as a url to a static file that is copied to the dist folder. You can then use this url in your injectable config.
137
- 2. The `fontInjectable` uses a plain url to a css file that we host normally on our site. In this case you would be responsible for copying the css file into your source code and making sure it is hosted at some location.
77
+ **Example:**
78
+ You will need to install `file-loader`, `extract-loader`, and `css-loader` and import the files you need like this:
138
79
 
139
80
  ```ts
140
81
  import readiumBefore from '!file-loader!extract-loader!css-loader!@nypl/web-reader/dist/injectable-html-styles/ReadiumCSS-before.css';
@@ -155,6 +96,8 @@ const cssInjectables: Injectable[] = [
155
96
  url: readiumAfter,
156
97
  },
157
98
  ];
99
+
100
+ // The `fontInjectable` uses a plain url to a css file that we host normally on our site. In this case you would be responsible for copying the css file into your source code and making sure it is hosted at some location.
158
101
  const fontInjectable: Injectable = {
159
102
  type: 'style',
160
103
  url: `${origin}/fonts/opendyslexic/opendyslexic.css`,
@@ -166,39 +109,106 @@ const htmlInjectables = [...cssInjectables, fontInjectable];
166
109
  const Reader = () => {
167
110
  return (
168
111
  <WebReader
169
- injectables={htmlInjectables}
112
+ injectablesReflowable={htmlInjectables}
170
113
  webpubManifestUrl="example/manifest.json"
171
114
  />
172
115
  );
173
116
  };
174
117
  ```
175
118
 
176
- **Note:** Injectables do not apply to pdf reading.
119
+ A real-world [example](https://github.com/NYPL/sfr-bookfinder-front-end/blob/development/src/components/ReaderLayout/ReaderLayout.tsx) can be seen in the Digital Research Books project.
120
+
121
+ Alternatively, you can host those three Readium CSS files within your project and import them wherever you render the <WebReader />. This is what we are doing in [Open eBooks](https://github.com/NYPL/ereading-clients/tree/staging/apps/oew/public/css/readium), which enables us to support offline reading mode.
122
+
123
+ ### injectablesFixed vs. injectablesReflowable
124
+
125
+ There are two different injectables props you can pass to the web reader.
126
+
127
+ - `injectablesReflowable` is used by HTML-based text books, or any content that makes sense to be resized based on the screen size. For most cases, this is where you should add the Readium CSS files mentioned above.
128
+ - `injectablesFixed` is being used in Open eBooks as a way to style picture books. It could also be used to style other formats like audio and video files.
129
+
130
+ Your app can provide both props or only one. The reader will decide which one to load into the iframe based on the book format defined in the webpub manifest.
131
+
132
+ ## Other Injectables
133
+
134
+ You can import and inject other files into the `<WebReader />` to customize behavior. For example, in Open eBooks, we import some [custom JavaScript](https://github.com/NYPL/ereading-clients/blob/staging/apps/oew/src/components/theme-ui/WebReader.tsx#L65) to disable right clicking & copying copywritten content.
135
+
136
+ ## Custom Styling
137
+
138
+ Basic default styling is included in the basic UI. We are using [Chakra](https://chakra-ui.com/) to style the default UI components. You can wrap our UI components in your own `<ThemeProvider>` to pass your own custom theme.
177
139
 
178
140
  ## Errors
179
141
 
180
- We make every effort to throw useful errors. Your app should probably wrap the web reader component in a React `<ErrorBoundary>` to either display the thrown errors or a custom error state for your users in the case one is thrown. See the example app for an example using an Error Boundary.
142
+ We make every effort to throw useful errors. Your app should probably wrap the web reader component in a React `<ErrorBoundary>` to either display the thrown errors or a custom error state for your users in the case one is thrown.
143
+
144
+ ## Modifying PDF Manifests
145
+
146
+ In some cases it may be desirable to modify the Webpub Manifest before passing it to the web reader. One example of this is with single-resource PDFs. These manifests sometimes arrive without a table of contents. The information for the TOC is embedded in the single PDF file itself. In this situation, we have set up a utility to extract the PDF TOC and add it to the manifest. There is an example of this working at [`/pdf/single-resource-short`](https://nypl-web-reader.vercel.app/pdf/single-resource-short).
147
+
148
+ In order to make this work in your app, you will want to:
149
+
150
+ 1. Fetch your manifest
151
+ 1. Use `addTocToManifest` to add the table of contents
152
+ 1. Generate a synthetic URL for the in-memory JSON object
153
+ 1. Pass this generated URL to the web reader.
154
+
155
+ <details>
156
+ <summary>See the example code</summary>
157
+
158
+ ```ts
159
+ const fetchAndModifyManifest: Fetcher<string, string> = async (url) => {
160
+ const response = await fetch(url);
161
+ const manifest = await response.json();
162
+ const modifiedManifest = await addTocToManifest(
163
+ manifest,
164
+ getProxiedResource(pdfProxyUrl),
165
+ pdfWorkerSrc
166
+ );
167
+ const syntheticUrl = URL.createObjectURL(
168
+ new Blob([JSON.stringify(modifiedManifest)])
169
+ );
170
+ return syntheticUrl;
171
+ };
181
172
 
182
- # Development
173
+ const SingleResourcePdf = () => {
174
+ const { data: modifiedManifestUrl, isLoading } = useSWR<string>(
175
+ '/samples/pdf/single-resource-short.json',
176
+ fetchAndModifyManifest
177
+ );
178
+
179
+ if (isLoading || !modifiedManifestUrl) return <div>Loading...</div>;
180
+
181
+ return (
182
+ <WebReader
183
+ webpubManifestUrl={modifiedManifestUrl}
184
+ proxyUrl={pdfProxyUrl}
185
+ pdfWorkerSrc={`${origin}/pdf-worker/pdf.worker.min.js`}
186
+ />
187
+ );
188
+ };
189
+ ```
190
+
191
+ </details>
183
192
 
184
193
  ## Architecture
185
194
 
186
- ### Overview
195
+ One of the primary architectural goals of the web reader is to abstract away the particularities of the many individual publication formats by using a common manifest to describe all publication types. This is the [Readium Webpub Manifest](https://readium.org/webpub-manifest/profiles/epub.html). This allows the web-reader to consume Webpub Manifests, not manipulate or generate them.
196
+
197
+ - ePubs are generally run through a Streamer, which fetches the full compressed ePub, generates a manifest for it, and then serves the individual pieces separately. Processing EPUBs into Webpub Manifests can be done with a Readium Streamer or some other way. [epub-to-webpub](https://github.com/NYPL/ePub-to-webpub) is used in the [Open eBooks](https://github.com/NYPL/ereading-clients/tree/staging/apps/oew) project and generates Webpub Manifests from EPUBs and the web-reader consumes them. Digital Research Books generates Webpub manifests for EPUBs using a [Python script](https://github.com/NYPL/drb-etl-pipeline/blob/main/managers/webpubManifest.py).
198
+ - On the PDF side, we don't have a shared utility for generating manifests, but the Digital Research Books project does use a [Python script](https://github.com/NYPL/drb-etl-pipeline/blob/main/managers/webpubManifest.py) to pre-generate PDF manifests from web-scraped content before sending them to the web-reader. These manifests are then stored in an S3 bucket.
187
199
 
188
- We always start with a Webpub Manifest, which gives us metadata and the structure of the publication with links to the content. Depending on the `metadata.conformsTo` field, we know which type of reader to use to render the publication. Each media type (HTML for EPUBS, PDF for PDF publications, etc) has its own `use_X_Reader` hook (`usePdfReader`, `useHtmlReader`, etc).
200
+ A Webpub Manifest gives us metadata and the structure of the publication with links to the content. Depending on the `metadata.conformsTo` field, we know which type of reader to use to render the publication. Each media type (HTML for EPUBS, PDF for PDF publications, etc) has its own `use_X_Reader` hook (`usePdfReader`, `useHtmlReader`, etc).
201
+
202
+ C4 Models have been created to demonstrate how the WebReader and other web reading utilities interact with the DRB & Open eBooks apps. View them here: https://structurizr.com/share/72104
189
203
 
190
204
  **Notes:**
191
205
 
192
206
  - There is one `use_X_Reader` per _media-type_ (PDF, HTML, Image, etc), not per _format_. As in, ePub and Mobi books are different formats that use the same media type (HTML). Audiobooks and PDF collections use different media types. We currently only have plans for HTML and PDF, but other hooks are welcome and should fit right in.
193
- - We always start from a Webpub Manifest. This means other formats (like ePub) need to be processed before they get to us. This can be done with a Readium Streamer, or some other way.
194
- - For example, DRB is pre-generating PDF manifests from web-scraped content.
195
- - There is [nypl/epub-to-webpub](https://github.com/NYPL/ePub-to-webpub) to generate Webpub Manifests from EPUBS.
196
- - ePubs are generally run through a Streamer, which is a piece that fetches the full compressed ePub, generates a manifest for it, and then serves the individual pieces separately.
197
- - AxisNow encrypted ePubs are served uncompressed. We will generate the manifest for them on the client before instantiating the reader.
207
+ - AxisNow encrypted ePubs are served uncompressed. We will generate the manifest for them on the client before instantiating the reader.
198
208
 
199
- ### Pieces of the architecture:
209
+ ### Pieces of the Architecture
200
210
 
201
- 1. **use_X_Reader hook**
211
+ 1. **use_X_Reader Hook**
202
212
 
203
213
  - Takes in the Webpub Manifest and returns:
204
214
  - `State` of the reader, such as current settings and location.
@@ -207,7 +217,7 @@ We always start with a Webpub Manifest, which gives us metadata and the structur
207
217
  - Internally, it will instantiate whatever package is being used to control that media type, and render the contents into the `Content` element it returns.
208
218
  - Each hook for each media type separately manages its own state using a redux-style `useReducer` hook. There is a basic set of common state that is shared and returned from the `use_X_Reader` hook, but custom internal state can also be added, such as the `D2Reader` instance in the `useHtmlReader` hook.
209
219
 
210
- 2. **useWebReader hook**
220
+ 2. **useWebReader Hook**
211
221
 
212
222
  - This is a generic hook that works for both PDF manifests and HTML-type manifests. It will internally call the proper `use_X_Reader` hook for you, and pass through the return value.
213
223
 
@@ -221,7 +231,7 @@ We always start with a Webpub Manifest, which gives us metadata and the structur
221
231
  This is the folder structure:
222
232
 
223
233
  ```txt
224
- /cypress # cypress tests will go in here
234
+ /cypress # cypress integration tests
225
235
  /example # example app packaged by Parcel
226
236
  index.html
227
237
  index.tsx # entrypoint for the demo app
@@ -235,9 +245,7 @@ This is the folder structure:
235
245
  types.ts # commonly used types
236
246
  useWebReader.tsx # the React hook providing the main API into the reader
237
247
  /test
238
- blah.test.tsx # tests will go in here
239
- /stories # stories will go in here
240
- /.storybook # storybook config
248
+ blah.test.tsx # unit tests go here
241
249
  ```
242
250
 
243
251
  ### Decryption
@@ -249,41 +257,37 @@ The web reader does support DRM via two possible routes:
249
257
 
250
258
  The `AxisNow Encrypted EPUB` example shows how this is done using the private NYPL AxisNow decryptor. The AxisNow scheme is a specific DRM technique not publicly available and the repo and code for the decryptor cannot be shared. Thus this example will not work for the public, but you can read the example code to see how we use the private `Decryptor` package to:
251
259
 
252
- - Create a Web Worker using Comlink](https://github.com/GoogleChromeLabs/comlink) that will performe the fetching and decryption. This should help keep the main thread free while those heavy tasks are performed.
260
+ - Create a Web Worker using [Comlink](https://github.com/GoogleChromeLabs/comlink) that will perform the fetching and decryption. This should help keep the main thread free while those heavy tasks are performed.
253
261
  - Fetch content from the network
254
262
  - Decrypt the HTML content
255
263
  - Search for embedded CSS and image assets
256
264
  - Fetch those assets and decrypt them
257
- - Re-embed the decrypted CSS and image assets as Object URLs into the decrypted HTML document.
258
- - Return the HTML string with fully decrypted reources for the web-reader to render in the iframe.
259
-
260
- ## Commands
265
+ - Re-embed the decrypted CSS and image assets as Object URLs into the decrypted HTML document
266
+ - Return the HTML string with fully decrypted reources for the web-reader to render in the iframe
261
267
 
262
- Before getting started, be sure to run `npm install`.
268
+ ## Getting Started
263
269
 
264
- The recommended workflow is to either run the storybook app, or the example app:
270
+ These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See [Publishing to NPM](#publishing-to-npm) for notes on how to publish a new version to NPM.
265
271
 
266
- ### Storybook
272
+ ### Prerequisites
267
273
 
268
- Run in `/web-reader`:
274
+ 1. Before getting started, be sure to run `npm install`.
269
275
 
270
- ```bash
271
- npm run start
272
- ```
276
+ If you are internal to NYPL you can be added to the private `@nypl-simplified-packages/axisnow-access-control-web` package, which is required in order to open the AxisNow Encrypted EPUB example. If you decide to install this package, you'll also need to generate a Personal Access Token in GitHub with `read` permissions and run `GH_PACKAGE_AUTH_TOKEN=<your-token-here> npm install`.
273
277
 
274
- Then in another terminal:
278
+ 2. In order to open any of the PDF examples in the Example App, you'll need to allow urls in a `WebpubManifest` to be proxied. This is done by passing a `proxyUrl` to the `<WebReader>` component. In order to do that, you must have a proxy running somewhere.
275
279
 
276
- ```bash
277
- npm run storybook
278
- ```
280
+ We have set up a small express-based CORS proxy that can be run for local development.
279
281
 
280
- This loads the stories from `./stories`.
282
+ - Run the proxy with `npm run cors-proxy`.
283
+ - Pass the proxy url to the example app by setting the following env var in a `.env` file at the root of the project: `CORS_PROXY_URL="http://localhost:3001/?requestUrl="`.
284
+ - In a separate terminal session, start the example app: `npm run example`.
281
285
 
282
- > NOTE: Stories should reference the components as if using the library. This means importing from the root project directory. This has been aliased in the tsconfig and the storybook webpack config as a helper.
286
+ 3. In order to open the AxisNow Encrypted EPUB example (`/html/axisnow-encrypted`), you will need to have `process.env.VAULT_UUID` and `process.env.ISBN` set in your `.env` file. Read [example/README.txt](/example/README.txt) for more info.
283
287
 
284
- ### Example
288
+ ### Running the Example App
285
289
 
286
- To run the example app:
290
+ To run the example app for local development:
287
291
 
288
292
  ```bash
289
293
  npm run example
@@ -291,31 +295,47 @@ npm run example
291
295
 
292
296
  The example will rebundle on change, but you have to refresh your browser to see changes (no hot reloading currently).
293
297
 
294
- #### Service Worker on localhost
298
+ ## Development Notes
295
299
 
296
- To develop with the service worker in the example app, you will need to run the app using HTTPS locally, and you will need to enable the service worker. We have disabled it by default because otherwise your development changes will never be reflected in the browser (since old JS will be served from the cache). You can run the app with https and the service worker enabled via the script:
300
+ ### Performance Optimizations
297
301
 
298
- ```
299
- npm run example:sw
302
+ Please see the main `tsdx` [optimizations docs](https://github.com/palmerhq/tsdx#optimizations). In particular, know that you can take advantage of development-only optimizations:
303
+
304
+ ```js
305
+ // ./types/index.d.ts
306
+ declare var __DEV__: boolean;
307
+
308
+ // inside your code...
309
+ if (__DEV__) {
310
+ console.log('foo');
311
+ }
300
312
  ```
301
313
 
302
- If this HTTPS setup doesn't work for you, you may need to follow [this guide](https://web.dev/how-to-use-local-https/) to generate your own certificates or trust ours.
314
+ You can also choose to install and use [invariant](https://github.com/palmerhq/tsdx#invariant) and [warning](https://github.com/palmerhq/tsdx#warning) functions.
303
315
 
304
- **NOTE:** Developing with the SW can be tricky. You will need to clear the CacheStorage of your browser whenever you make changes to your JS in dev mode. Hard refreshing your browser is not enough. I also suggest enabling `update on reload` in Chrome dev tools under `Application>Service Worker`.
316
+ ### Module Formats
305
317
 
306
- ### CORS Proxy
318
+ CJS and ESModules module formats are supported.
307
319
 
308
- We sometimes run in to CORS errors, and have a system to allow urls in a `WebpubManifest` to be proxied. This is done by passing a `proxyUrl` to the `<WebReader>` component. In order to do that, you must have a proxy running somewhere.
320
+ The appropriate paths are configured in `package.json` and `dist/index.js` accordingly. Please [report](https://github.com/NYPL-Simplified/web-reader/issues) if any issues are found.
309
321
 
310
- #### CORS Proxy in the Example App
322
+ ## Running the Tests
311
323
 
312
- I have set up a small express-based CORS proxy that can be run for local development.
324
+ ### Unit Tests with Jest
313
325
 
314
- - Run the proxy with `npm run cors-proxy`.
315
- - Pass the proxy url to the example app by setting the following env var in a `.env` file at the root of the project: `CORS_PROXY_URL="http://localhost:3001/?requestUrl="`.
316
- - In a separate terminal session, start the example app: `npm run example`.
326
+ To run Jest in watch mode:
327
+
328
+ ```bash
329
+ npm run test
330
+ ```
331
+
332
+ To run all the tests as they run in CI:
333
+
334
+ ```bash
335
+ npm run test:ci
336
+ ```
317
337
 
318
- ### Cypress
338
+ ### Integration Tests with Cypress
319
339
 
320
340
  The tests we have are located in the `cypress/integration` folder.
321
341
 
@@ -333,60 +353,30 @@ To run tests on your terminal without a browser:
333
353
  npm run cypress:cli
334
354
  ```
335
355
 
336
- ### Other Scripts
337
-
338
- - `npm run test` - to run Jest in watch mode.
339
- - `npm run size` - to calculate the real cost of the library for consumers (using [size-limit](https://github.com/ai/size-limit)).
340
- - `npm run analyze` - to analyze the library bundle for places we can shrink down.
341
-
342
- ## Code Quality
356
+ ## Code Quality & Bundle Size Checks
343
357
 
344
- Code quality enforcement is set up with `prettier`, `husky`, and `lint-staged`. Run `npm run lint` to lint the code, or have you editor do it.
345
-
346
- ## Styling / CSS
347
-
348
- We are using [Chakra](https://chakra-ui.com/) to style our default UI components. You can wrap our UI components in your own `<ThemeProvider>` to pass your own custom theme.
358
+ - `npm run lint` to lint the code yourself. Code quality enforcement is set up in pre-commit hooks with `prettier`, `husky`, and `lint-staged`.
359
+ - `npm run size` - to calculate the real cost of the library for consumers (using [size-limit](https://github.com/ai/size-limit))
360
+ - `npm run analyze` - to analyze the library bundle for places we can shrink down
349
361
 
350
362
  ## Continuous Integration
351
363
 
352
- ### GitHub Actions
353
-
354
- There are two Github Workflows:
355
-
356
- - `main` which installs deps w/ cache, lints, tests, and builds on all pushes against a Node and OS matrix
357
- - `size` which comments cost comparison of your library on every pull request using [size-limit](https://github.com/ai/size-limit)
358
-
359
- ## Optimizations
360
-
361
- Please see the main `tsdx` [optimizations docs](https://github.com/palmerhq/tsdx#optimizations). In particular, know that you can take advantage of development-only optimizations:
362
-
363
- ```js
364
- // ./types/index.d.ts
365
- declare var __DEV__: boolean;
366
-
367
- // inside your code...
368
- if (__DEV__) {
369
- console.log('foo');
370
- }
371
- ```
364
+ There are three Github Actions Workflows:
372
365
 
373
- You can also choose to install and use [invariant](https://github.com/palmerhq/tsdx#invariant) and [warning](https://github.com/palmerhq/tsdx#warning) functions.
366
+ - `main` - installs deps w/ cache, lints, tests, and builds on all pushes against a Node and OS matrix
367
+ - `size` - comments cost comparison of your library on every pull request using [size-limit](https://github.com/ai/size-limit)
368
+ - `cypress` - runs the Cypress integration tests on Vercel deployments, which means it runs whenever a pull request is opened or updated
374
369
 
375
- ## Module Formats
370
+ ## Versioning
376
371
 
377
- CJS and ESModules module formats are supported.
372
+ We use [SemVer](https://semver.org/) for versioning. For the versions available, see the [releases](https://github.com/NYPL-Simplified/web-reader/releases) on this repository.
378
373
 
379
- The appropriate paths are configured in `package.json` and `dist/index.js` accordingly. Please report if any issues are found.
380
-
381
- ## Deploying the Example Playground
374
+ ## Publishing to NPM
382
375
 
383
- The Playground is just a simple [Parcel](https://parceljs.org) app, you can deploy it anywhere you would normally deploy that. We have deployed it to Vercel. Here is how you build a standalone version:
376
+ To publish the package to [NPM](https://www.npmjs.com/package/@nypl/web-reader), you must first be added to the `nypl` organization within NPM.
384
377
 
385
- ```bash
386
- cd example # if not already in the example folder
387
- npm run build # builds to dist
388
- ```
378
+ Run `npm run release` from the main branch. Follow the prompts to add a new version, publish a release to github, and push to npm.
389
379
 
390
- ## Publishing to NPM
380
+ ## License
391
381
 
392
- Run `npm run release` from the main branch. Follow the prompts to add a new version, publish a release to github, and push to npm.
382
+ This project is licensed under the MIT License - see the [LICENSE](/LICENSE) file for details.