forked from sheetjs/docs.sheetjs.com
		
	new-dataset-url
This commit is contained in:
		
							parent
							
								
									990f42934b
								
							
						
					
					
						commit
						5d87422b97
					
				| @ -1064,7 +1064,7 @@ When the app is loaded, the data will be displayed in rows. | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| [^1]: <https://catalog.data.gov/dataset/national-student-loan-data-system-aa85f> is the current location for the CC0-licensed dataset. `PortfolioSummary.xls` is the file name. | ||||
| [^1]: The dataset URL has changed many times over the years. The current location for the CC0-licensed dataset can be found by [searching for "National Student Loan Data System" on `data.gov`](https://catalog.data.gov/dataset/?q=national+student+loan+data+system&publisher=Office+of+Federal+Student+Aid+%28FSA%29&organization=ed-gov). `PortfolioSummary.xls` is the file name within the dataset. | ||||
| [^2]: See [`read` in "Reading Files"](/docs/api/parse-options) | ||||
| [^3]: See ["SheetJS Data Model"](/docs/csf/) | ||||
| [^4]: See ["Workbook Object"](/docs/csf/book) | ||||
|  | ||||
| @ -159,12 +159,19 @@ import { read, utils } from 'xlsx'; | ||||
| 
 | ||||
| /* Fetch and update the state once */ | ||||
| useEffect(() => { (async() => { | ||||
|   /* Download file */ | ||||
|   const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer(); | ||||
|   /* Download from https://sheetjs.com/pres.numbers */ | ||||
|   const f = await fetch("https://sheetjs.com/pres.numbers"); | ||||
|   const ab = await f.arrayBuffer(); | ||||
| 
 | ||||
|   // highlight-start | ||||
|   const wb = read(f); // parse the array buffer | ||||
|   /* parse */ | ||||
|   const wb = read(ab); | ||||
| 
 | ||||
|   /* generate array of objects from first worksheet */ | ||||
|   const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet | ||||
|   const data = utils.sheet_to_json(ws); // generate objects | ||||
| 
 | ||||
|   /* update state */ | ||||
|   setPres(data); // update state | ||||
|   // highlight-end | ||||
| })(); }, []); | ||||
| @ -179,12 +186,19 @@ import { read, utils } from 'xlsx'; | ||||
| 
 | ||||
| /* Fetch and update the state once */ | ||||
| useEffect(() => { (async() => { | ||||
|   /* Download file */ | ||||
|   const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer(); | ||||
|   /* Download from https://sheetjs.com/pres.numbers */ | ||||
|   const f = await fetch("https://sheetjs.com/pres.numbers"); | ||||
|   const ab = await f.arrayBuffer(); | ||||
| 
 | ||||
|   // highlight-start | ||||
|   const wb = read(f); // parse the array buffer | ||||
|   /* parse */ | ||||
|   const wb = read(ab); | ||||
| 
 | ||||
|   /* generate array of presidents from the first worksheet */ | ||||
|   const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet | ||||
|   const data: President[] = utils.sheet_to_json<President>(ws); // generate objects | ||||
| 
 | ||||
|   /* update state */ | ||||
|   setPres(data); // update state | ||||
|   // highlight-end | ||||
| })(); }, []); | ||||
| @ -305,6 +319,57 @@ export default function SheetJSReactAoO() { | ||||
| 
 | ||||
| <details open><summary><b>How to run the example</b> (click to show)</summary> | ||||
| 
 | ||||
| <Tabs groupId="starter"> | ||||
|   <TabItem name="vite" value="ViteJS"> | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was last tested on 2023 October 08 with ViteJS 4.4.1 and React 18.2.0 | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| 1) Create a new site: | ||||
| 
 | ||||
| ```bash | ||||
| npm create vite@latest sheetjs-react -- --template react | ||||
| ``` | ||||
| 
 | ||||
| 2) Install the SheetJS dependency and start the dev server: | ||||
| 
 | ||||
| <CodeBlock language="bash">{`\ | ||||
| cd sheetjs-react | ||||
| npm i | ||||
| npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz | ||||
| npm run dev`} | ||||
| </CodeBlock> | ||||
| 
 | ||||
| 3) Open a web browser and access the displayed URL (`http://localhost:5173`) | ||||
| 
 | ||||
| 4) Replace `src/App.jsx` with the `src/SheetJSReactAoO.js` example. | ||||
| 
 | ||||
| The page will refresh and show a table with an Export button.  Click the button | ||||
| and the page will attempt to download `SheetJSReactAoA.xlsx`. | ||||
| 
 | ||||
| 5) Build the site: | ||||
| 
 | ||||
| ```bash | ||||
| npm run build | ||||
| ``` | ||||
| 
 | ||||
| The generated site will be placed in the `dist` folder. | ||||
| 
 | ||||
| 6) Start a local web server: | ||||
| 
 | ||||
| ```bash | ||||
| npx http-server dist | ||||
| ``` | ||||
| 
 | ||||
| Access the displayed URL (typically `http://localhost:8080`) with a web browser | ||||
| and test the page. | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem name="CRA" value="create-react-app"> | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was last run on 2023 July 03 using `create-react-app@5.0.1` and | ||||
| @ -334,8 +399,25 @@ npm start`} | ||||
| The page will refresh and show a table with an Export button.  Click the button | ||||
| and the page will attempt to download `SheetJSReactAoA.xlsx`. | ||||
| 
 | ||||
| 5) Build the site with `npm run build`, then test with `npx http-server build`. | ||||
| Access `http://localhost:8080` with a web browser to test the bundled site. | ||||
| 5) Build the site: | ||||
| 
 | ||||
| ```bash | ||||
| npm run build | ||||
| ``` | ||||
| 
 | ||||
| The generated site will be placed in the `build` folder. | ||||
| 
 | ||||
| 6) Start a local web server: | ||||
| 
 | ||||
| ```bash | ||||
| npx http-server build | ||||
| ``` | ||||
| 
 | ||||
| Access the displayed URL (typically `http://localhost:8080`) with a web browser | ||||
| and test the page. | ||||
| 
 | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| @ -394,6 +476,57 @@ export default function SheetJSReactHTML() { | ||||
| 
 | ||||
| <details open><summary><b>How to run the example</b> (click to show)</summary> | ||||
| 
 | ||||
| <Tabs groupId="starter"> | ||||
|   <TabItem name="vite" value="ViteJS"> | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was last tested on 2023 October 08 with ViteJS 4.4.1 and React 18.2.0 | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| 1) Create a new site: | ||||
| 
 | ||||
| ```bash | ||||
| npm create vite@latest sheetjs-react -- --template react | ||||
| ``` | ||||
| 
 | ||||
| 2) Install the SheetJS dependency and start the dev server: | ||||
| 
 | ||||
| <CodeBlock language="bash">{`\ | ||||
| cd sheetjs-react | ||||
| npm i | ||||
| npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz | ||||
| npm run dev`} | ||||
| </CodeBlock> | ||||
| 
 | ||||
| 3) Open a web browser and access the displayed URL (`http://localhost:5173`) | ||||
| 
 | ||||
| 4) Replace `src/App.jsx` with the `src/SheetJSReactHTML.js` example. | ||||
| 
 | ||||
| The page will refresh and show a table with an Export button.  Click the button | ||||
| and the page will attempt to download `SheetJSReactHTML.xlsx`. | ||||
| 
 | ||||
| 5) Build the site: | ||||
| 
 | ||||
| ```bash | ||||
| npm run build | ||||
| ``` | ||||
| 
 | ||||
| The generated site will be placed in the `dist` folder. | ||||
| 
 | ||||
| 6) Start a local web server: | ||||
| 
 | ||||
| ```bash | ||||
| npx http-server dist | ||||
| ``` | ||||
| 
 | ||||
| Access the displayed URL (typically `http://localhost:8080`) with a web browser | ||||
| and test the page. | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem name="CRA" value="create-react-app"> | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was last run on 2023 July 03 using `create-react-app@5.0.1` and | ||||
| @ -423,8 +556,25 @@ npm start`} | ||||
| The page will refresh and show a table with an Export button.  Click the button | ||||
| and the page will attempt to download `SheetJSReactHTML.xlsx`. | ||||
| 
 | ||||
| 5) Build the site with `npm run build`, then test with `npx http-server build`. | ||||
| Access `http://localhost:8080` with a web browser to test the bundled site. | ||||
| 5) Build the site: | ||||
| 
 | ||||
| ```bash | ||||
| npm run build | ||||
| ``` | ||||
| 
 | ||||
| The generated site will be placed in the `build` folder. | ||||
| 
 | ||||
| 6) Start a local web server: | ||||
| 
 | ||||
| ```bash | ||||
| npx http-server build | ||||
| ``` | ||||
| 
 | ||||
| Access the displayed URL (typically `http://localhost:8080`) with a web browser | ||||
| and test the page. | ||||
| 
 | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
|  | ||||
| @ -54,27 +54,17 @@ depends on the application. | ||||
| Typically, some users will create a spreadsheet with source data that should be | ||||
| loaded into the site.  This sheet will have known columns. | ||||
| 
 | ||||
| For example, our [presidents sheet](https://sheetjs.com/pres.xlsx) has "Name" and "Index" columns: | ||||
| #### State | ||||
| 
 | ||||
| The example [presidents sheet](https://sheetjs.com/pres.xlsx) has one header row | ||||
| with "Name" and "Index" columns. The natural JS representation is an object for | ||||
| each row, using the values in the first rows as keys: | ||||
| 
 | ||||
| <table><thead><tr><th>Spreadsheet</th><th>State</th></tr></thead><tbody><tr><td> | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| This naturally maps to an array of typed objects, as in the TS example below: | ||||
| 
 | ||||
| ```ts | ||||
| import { read, utils } from 'xlsx'; | ||||
| 
 | ||||
| interface President { | ||||
|   Name: string; | ||||
|   Index: number; | ||||
| } | ||||
| 
 | ||||
| const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer(); | ||||
| const wb = read(f); | ||||
| const data = utils.sheet_to_json<President>(wb.Sheets[wb.SheetNames[0]]); | ||||
| console.log(data); | ||||
| ``` | ||||
| 
 | ||||
| `data` will be an array of objects: | ||||
| </td><td> | ||||
| 
 | ||||
| ```js | ||||
| [ | ||||
| @ -86,8 +76,234 @@ console.log(data); | ||||
| ] | ||||
| ``` | ||||
| 
 | ||||
| A component will typically map over the data. The following example generates | ||||
| a TABLE with a row for each President: | ||||
| </td></tr></tbody></table> | ||||
| 
 | ||||
| Using the VueJS Composition API, the `ref`[^1] function creates state objects: | ||||
| 
 | ||||
| <Tabs groupId="lang"> | ||||
|   <TabItem name="JS" value="JavaScript"> | ||||
| 
 | ||||
| ```html | ||||
| <script setup> | ||||
| import { ref } from "vue"; | ||||
| 
 | ||||
| /* the component state is an array of objects */ | ||||
| const pres = ref([]); | ||||
| </script> | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem name="TS" value="TypeScript" default> | ||||
| 
 | ||||
| ```html | ||||
| <script setup lang="ts"> | ||||
| import { ref } from "vue"; | ||||
| 
 | ||||
| /* the component state is an array of objects */ | ||||
| const pres = ref<any[]>([]); | ||||
| </script> | ||||
| ``` | ||||
| 
 | ||||
| When the spreadsheet header row is known ahead of time, row typing is possible: | ||||
| 
 | ||||
| ```html | ||||
| <script setup lang="ts"> | ||||
| import { ref } from "vue"; | ||||
| 
 | ||||
| interface President { | ||||
|   Name: string; | ||||
|   Index: number; | ||||
| } | ||||
| 
 | ||||
| /* the component state is an array of presidents */ | ||||
| const pres = ref<President[]>([]); | ||||
| </script> | ||||
| ``` | ||||
| 
 | ||||
| :::caution pass | ||||
| 
 | ||||
| The types are informative. They do not enforce that worksheets include the named | ||||
| columns. A runtime data validation library should be used to verify the dataset. | ||||
| 
 | ||||
| When the file header is not known in advance, `any` should be used. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| #### Updating State | ||||
| 
 | ||||
| The SheetJS [`read`](/docs/api/parse-options) and [`sheet_to_json`](/docs/api/utilities/array#array-output) | ||||
| functions simplify state updates. They are best used in the function bodies of | ||||
| lifecycle hooks including `onMounted`[^2]. | ||||
| 
 | ||||
| The `onMounted` hook can download and update state when a person loads the site: | ||||
| 
 | ||||
| ```mermaid | ||||
| flowchart LR | ||||
|   url[(Remote\nFile)] | ||||
|   ab[(Data\nArrayBuffer)] | ||||
|   wb(SheetJS\nWorkbook) | ||||
|   ws(SheetJS\nWorksheet) | ||||
|   aoo(array of\nobjects) | ||||
|   state((component\nstate)) | ||||
|   url --> |fetch\n\n| ab | ||||
|   ab --> |read\n\n| wb | ||||
|   wb --> |wb.Sheets\nselect sheet| ws | ||||
|   ws --> |sheet_to_json\n\n| aoo | ||||
|   aoo --> |setPres\nfrom `setState`| state | ||||
| ``` | ||||
| 
 | ||||
| <Tabs groupId="lang"> | ||||
|   <TabItem name="JS" value="JavaScript"> | ||||
| 
 | ||||
| ```html | ||||
| <script setup> | ||||
| import { ref, onMounted } from "vue"; | ||||
| import { read, utils } from 'xlsx'; | ||||
| 
 | ||||
| /* the component state is an array of objects */ | ||||
| const pres = ref([]); | ||||
| 
 | ||||
| /* Fetch and update the state once */ | ||||
| onMounted(async() => { | ||||
|   /* Download from https://sheetjs.com/pres.numbers */ | ||||
|   const f = await fetch("https://sheetjs.com/pres.numbers"); | ||||
|   const ab = await f.arrayBuffer(); | ||||
| 
 | ||||
|   // highlight-start | ||||
|   /* parse */ | ||||
|   const wb = read(ab); | ||||
| 
 | ||||
|   /* generate array of objects from first worksheet */ | ||||
|   const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet | ||||
|   const data = utils.sheet_to_json(ws); // generate objects | ||||
| 
 | ||||
|   /* update state */ | ||||
|   pres.value = data; | ||||
|   // highlight-end | ||||
| }); | ||||
| </script> | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem name="TS" value="TypeScript" default> | ||||
| 
 | ||||
| ```html | ||||
| <script setup lang="ts"> | ||||
| import { ref, onMounted } from "vue"; | ||||
| import { read, utils } from 'xlsx'; | ||||
| 
 | ||||
| interface President { | ||||
|   Name: string; | ||||
|   Index: number; | ||||
| } | ||||
| 
 | ||||
| /* the component state is an array of presidents */ | ||||
| const pres = ref<President[]>([]); | ||||
| 
 | ||||
| /* Fetch and update the state once */ | ||||
| onMounted(async() => { | ||||
|   /* Download from https://sheetjs.com/pres.numbers */ | ||||
|   const f = await fetch("https://sheetjs.com/pres.numbers"); | ||||
|   const ab = await f.arrayBuffer(); | ||||
| 
 | ||||
|   // highlight-start | ||||
|   /* parse */ | ||||
|   const wb = read(ab); | ||||
| 
 | ||||
|   /* generate array of presidents from the first worksheet */ | ||||
|   const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet | ||||
|   const data: President[] = utils.sheet_to_json<President>(ws); // generate objects | ||||
| 
 | ||||
|   /* update state */ | ||||
|   pres.value = data; | ||||
|   // highlight-end | ||||
| }); | ||||
| </script> | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| #### Rendering Data | ||||
| 
 | ||||
| A component will typically map over the data with `v-for`[^3]. The following | ||||
| example generates a TABLE with a row for each President: | ||||
| 
 | ||||
| ```html title="Example SFC for displaying arrays of objects" | ||||
| <script setup> | ||||
| import { ref } from "vue"; | ||||
| const rows = ref([]); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
| <table> | ||||
|   <!-- The `thead` section includes the table header row --> | ||||
|   <thead><th>Name</th><th>Index</th></thead> | ||||
|   <!-- The `tbody` section includes the data rows --> | ||||
|   <tbody> | ||||
|     <!-- generate row (TR) for each president --> | ||||
|     <!-- highlight-start--> | ||||
|     <tr v-for="(row, idx) in rows" :key="idx"> | ||||
|       <td>{{ row.Name }}</td> | ||||
|       <td>{{ row.Index }}</td> | ||||
|     </tr> | ||||
|     <!-- highlight-end--> | ||||
|   </tbody> | ||||
| </table> | ||||
| </template> | ||||
| ``` | ||||
| 
 | ||||
| #### Exporting Data | ||||
| 
 | ||||
| The [`writeFile`](/docs/api/write-options) and [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input) | ||||
| functions simplify exporting data. They are best used in the function bodies of | ||||
| `v-on` event handlers like `@click`[^4]. | ||||
| 
 | ||||
| A callback can generate a local file when a user clicks a button: | ||||
| 
 | ||||
| ```mermaid | ||||
| flowchart LR | ||||
|   state((component\nstate)) | ||||
|   ws(SheetJS\nWorksheet) | ||||
|   wb(SheetJS\nWorkbook) | ||||
|   file[(XLSX\nexport)] | ||||
|   state --> |json_to_sheet\n\n| ws | ||||
|   ws --> |book_new\nbook_append_sheet| wb | ||||
|   wb --> |writeFile\n\n| file | ||||
| ``` | ||||
| 
 | ||||
| ```html | ||||
| <script setup> | ||||
| import { ref } from "vue"; | ||||
| import { utils, writeFileXLSX } from 'xlsx'; | ||||
| 
 | ||||
| const pres = ref([]); | ||||
| 
 | ||||
| /* get state data and export to XLSX */ | ||||
| function exportFile() { | ||||
|   /* generate worksheet from state */ | ||||
|   // highlight-next-line | ||||
|   const ws = utils.json_to_sheet(pres.value); | ||||
|   /* create workbook and append worksheet */ | ||||
|   const wb = utils.book_new(); | ||||
|   utils.book_append_sheet(wb, ws, "Data"); | ||||
|   /* export to XLSX */ | ||||
|   writeFileXLSX(wb, "SheetJSVueAoO.xlsx"); | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <button @click="exportFile">Export XLSX</button> | ||||
| </template> | ||||
| ``` | ||||
| 
 | ||||
| #### Complete Component | ||||
| 
 | ||||
| This complete component example fetches a test file and displays the contents in | ||||
| a HTML table. When the export button is clicked, a callback will export a file: | ||||
| 
 | ||||
| ```html title="src/SheetJSVueAoO.vue" | ||||
| <script setup> | ||||
| @ -157,8 +373,22 @@ The page will refresh and show a table with an Export button.  Click the button | ||||
| and the page will attempt to download `SheetJSVueAoA.xlsx`. There may be a delay | ||||
| since Vite will try to optimize the SheetJS library on the fly. | ||||
| 
 | ||||
| 5) Build the site with `npm run build`, then test with `npx http-server dist`. | ||||
| Access `http://localhost:8080` with a web browser to test the bundled site. | ||||
| 5) Build the site: | ||||
| 
 | ||||
| ```bash | ||||
| npm run build | ||||
| ``` | ||||
| 
 | ||||
| The generated site will be placed in the `dist` folder. | ||||
| 
 | ||||
| 6) Start a local web server: | ||||
| 
 | ||||
| ```bash | ||||
| npx http-server dist | ||||
| ``` | ||||
| 
 | ||||
| Access the displayed URL (typically `http://localhost:8080`) with a web browser | ||||
| and test the page. | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| @ -168,9 +398,14 @@ The main disadvantage of the Array of Objects approach is the specific nature | ||||
| of the columns.  For more general use, passing around an Array of Arrays works. | ||||
| However, this does not handle merge cells well! | ||||
| 
 | ||||
| The `sheet_to_html` function generates HTML that is aware of merges and other | ||||
| worksheet features.  VueJS `v-html` attribute allows assignment of `innerHTML` | ||||
| attribute, effectively inserting the code into the page: | ||||
| The [`sheet_to_html`](/docs/api/utilities/html#html-table-output) function | ||||
| generates HTML that is aware of merges and other worksheet features.  VueJS | ||||
| `v-html`[^5] attribute allows code to set the `innerHTML` attribute, effectively | ||||
| inserting the code into the page. | ||||
| 
 | ||||
| In this example, the component attaches a `ref` to the `DIV` container. During | ||||
| export, the first `TABLE` child element can be parsed with [`table_to_book`](/docs/api/utilities/html#html-table-input) to | ||||
| generate a workbook object. | ||||
| 
 | ||||
| ```html title="src/SheetJSVueHTML.vue" | ||||
| <script setup> | ||||
| @ -233,8 +468,40 @@ The page will refresh and show a table with an Export button.  Click the button | ||||
| and the page will attempt to download `SheetJSVueHTML.xlsx`. There may be a delay | ||||
| since Vite will try to optimize the SheetJS library on the fly. | ||||
| 
 | ||||
| 5) Build the site with `npm run build`, then test with `npx http-server dist`. | ||||
| Access `http://localhost:8080` with a web browser to test the bundled site. | ||||
| 5) Build the site: | ||||
| 
 | ||||
| ```bash | ||||
| npm run build | ||||
| ``` | ||||
| 
 | ||||
| The generated site will be placed in the `dist` folder. | ||||
| 
 | ||||
| 6) Start a local web server: | ||||
| 
 | ||||
| ```bash | ||||
| npx http-server dist | ||||
| ``` | ||||
| 
 | ||||
| Access the displayed URL (typically `http://localhost:8080`) with a web browser | ||||
| and test the page. | ||||
| 
 | ||||
| 
 | ||||
| 5) Build the site: | ||||
| 
 | ||||
| ```bash | ||||
| npm run build | ||||
| ``` | ||||
| 
 | ||||
| The generated site will be placed in the `dist` folder. | ||||
| 
 | ||||
| 6) Start a local web server: | ||||
| 
 | ||||
| ```bash | ||||
| npx http-server dist | ||||
| ``` | ||||
| 
 | ||||
| Access the displayed URL (typically `http://localhost:8080`) with a web browser | ||||
| and test the page. | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| @ -284,3 +551,9 @@ The entire demo is designed to run in Internet Explorer and does not reflect | ||||
| modern design patterns. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| [^1]: See [`ref()`](https://vuejs.org/api/reactivity-core.html#ref) in the VueJS documentation. | ||||
| [^2]: See [`onMounted()`](https://vuejs.org/api/composition-api-lifecycle.html#onmounted) in the VueJS documentation. | ||||
| [^3]: See [`v-for`](https://vuejs.org/api/built-in-directives.html#v-for) in the VueJS documentation. | ||||
| [^4]: See [`v-on`](https://vuejs.org/api/built-in-directives.html#v-on) in the VueJS documentation. | ||||
| [^5]: See [`v-html`](https://vuejs.org/api/built-in-directives.html#v-html) in the VueJS documentation. | ||||
| @ -1,5 +1,7 @@ | ||||
| --- | ||||
| title: GatsbyJS | ||||
| title: Spreadsheets in GatsbyJS Sites | ||||
| sidebar_label: GatsbyJS | ||||
| description: Make static websites from spreadsheets using GatsbyJS. Seamlessly integrate data into your website using SheetJS. Generate websites from data in Excel spreadsheets. | ||||
| pagination_prev: demos/net/index | ||||
| pagination_next: demos/mobile/index | ||||
| sidebar_custom_props: | ||||
| @ -7,21 +9,27 @@ sidebar_custom_props: | ||||
| --- | ||||
| 
 | ||||
| import current from '/version.js'; | ||||
| import Tabs from '@theme/Tabs'; | ||||
| import TabItem from '@theme/TabItem'; | ||||
| import CodeBlock from '@theme/CodeBlock'; | ||||
| 
 | ||||
| [GatsbyJS](https://www.gatsbyjs.com/) is a framework for creating websites. It | ||||
| uses React components for page templates and GraphQL for loading data. | ||||
| GatsbyJS is a framework for creating websites. It uses React components for page | ||||
| templates and GraphQL for loading data. | ||||
| 
 | ||||
| [`gatsby-transformer-excel`](https://www.gatsbyjs.com/plugins/gatsby-transformer-excel/) | ||||
| is a transformer that generates GraphQL nodes for each row of each worksheet. | ||||
| The plugin is officially supported by the Gatsby team. The plugin documentation | ||||
| includes examples and more detailed usage instructions. | ||||
| [SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing | ||||
| data from spreadsheets. | ||||
| 
 | ||||
| :::note pass | ||||
| This demo uses GatsbyJS and SheetJS (through the `gatsby-transformer-excel`[^1] | ||||
| transformer) to pull data from a spreadsheet and display the content in a page. | ||||
| 
 | ||||
| The ["Complete Example"](#complete-example) section includes a complete website | ||||
| powered by an XLSX spreadsheet. | ||||
| 
 | ||||
| :::info pass | ||||
| 
 | ||||
| `gatsby-transformer-excel` is maintained by the Gatsby core team and all bugs | ||||
| should be directed to the main Gatsby project.  If it is determined to be a bug | ||||
| in the parsing logic, issues should then be raised with the SheetJS project. | ||||
| in the parsing logic, issues should then be raised with the SheetJS team. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| @ -30,7 +38,7 @@ in the parsing logic, issues should then be raised with the SheetJS project. | ||||
| `gatsby-transformer-excel` uses an older version of the library.  It can be | ||||
| overridden through a `package.json` override in the latest versions of NodeJS: | ||||
| 
 | ||||
| <CodeBlock language="json">{`\ | ||||
| <CodeBlock language="json" title="package.json (add highlighted lines)">{`\ | ||||
| { | ||||
|   "overrides": { | ||||
|     "xlsx": "https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz" | ||||
| @ -40,11 +48,93 @@ overridden through a `package.json` override in the latest versions of NodeJS: | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ## GraphQL details | ||||
| :::warning Telemetry | ||||
| 
 | ||||
| `gatsby-transformer-excel` generates nodes for each data row of each worksheet. | ||||
| Under the hood, it uses [`sheet_to_json`](/docs/api/utilities#array-output) | ||||
| to generate row objects using the headers in the first row as keys. | ||||
| GatsbyJS collects telemetry by default. The `telemetry` subcommand can disable it: | ||||
| 
 | ||||
| ```js | ||||
| npx gatsby telemetry --disable | ||||
| ``` | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ## Integration Details | ||||
| 
 | ||||
| ```mermaid | ||||
| flowchart LR | ||||
|   file[(workbook\nfile)] | ||||
|   subgraph SheetJS operations | ||||
|     filenode[File\nNode] | ||||
|     datanode[Data\nNodes] | ||||
|   end | ||||
|   aoo(array of\nobjects) | ||||
|   html{{HTML\nTABLE}} | ||||
|   file --> |Source\nPlugin| filenode | ||||
|   filenode --> |Transformer\nPlugin| datanode | ||||
|   datanode --> |GraphQL\nQuery| aoo | ||||
|   aoo --> |React\nJSX| html | ||||
| ``` | ||||
| 
 | ||||
| In the GatsbyJS data system, source plugins read from data sources and generate | ||||
| nodes represent raw data. Transformer plugins transform these nodes into other | ||||
| nodes that represent processed data for use in pages. | ||||
| 
 | ||||
| This example uses `gatsby-source-filesystem`[^2] to read files from the | ||||
| filesystem and `gatsby-transformer-excel` transformer to perform the transform. | ||||
| 
 | ||||
| ### Installation | ||||
| 
 | ||||
| The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) will be | ||||
| referenced by `gatsby-transformer-excel`. | ||||
| 
 | ||||
| Before installing, to ensure that the transformer uses the latest version of the | ||||
| library, the `overrides` section must be added to `package.json`: | ||||
| 
 | ||||
| <CodeBlock language="json" title="package.json (add highlighted lines)">{`\ | ||||
| { | ||||
|   // highlight-start | ||||
|   "overrides": { | ||||
|     "xlsx": "https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz" | ||||
|   } | ||||
|   // highlight-end | ||||
| }`} | ||||
| </CodeBlock> | ||||
| 
 | ||||
| `gatsby-transformer-excel` and `gatsby-source-filesystem` should be installed | ||||
| after installing SheetJS modules: | ||||
| 
 | ||||
| <Tabs groupId="pm"> | ||||
|   <TabItem value="npm" label="npm"> | ||||
| <CodeBlock language="bash">{`\ | ||||
| npx gatsby telemetry --disable | ||||
| npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz | ||||
| npm i --save gatsby-transformer-excel gatsby-source-filesystem`} | ||||
| </CodeBlock> | ||||
|   </TabItem> | ||||
|   <TabItem value="pnpm" label="pnpm"> | ||||
| <CodeBlock language="bash">{`\ | ||||
| npx gatsby telemetry --disable | ||||
| pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz | ||||
| pnpm install gatsby-transformer-excel gatsby-source-filesystem`} | ||||
| </CodeBlock> | ||||
|   </TabItem> | ||||
|   <TabItem value="yarn" label="Yarn" default> | ||||
| <CodeBlock language="bash">{`\ | ||||
| npx gatsby telemetry --disable | ||||
| yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz | ||||
| yarn add gatsby-transformer-excel gatsby-source-filesystem`} | ||||
| </CodeBlock> | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| ### GraphQL details | ||||
| 
 | ||||
| Under the hood, `gatsby-transformer-excel` uses the SheetJS `read`[^3] method to | ||||
| parse the workbook into a SheetJS workbook[^4]. Each worksheet is extracted from | ||||
| the workbook. The `sheet_to_json` method[^5] generates row objects using the | ||||
| headers in the first row as keys. | ||||
| 
 | ||||
| Consider the following worksheet: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| @ -78,18 +168,28 @@ The following query pulls the `Name` and `Index` fields from each row: | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ## GatsbyJS Demo | ||||
| ## Complete Example | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was tested on 2023 September 13 against `create-gatsby@3.12.0`. The | ||||
| generated project used `gatsby@5.12.4` and `react@18.2.0`. | ||||
| This demo was tested on 2023 October 08 against `create-gatsby@3.12.0`. The | ||||
| generated project used `gatsby@5.12.5` and `react@18.2.0`. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ### Project setup | ||||
| 
 | ||||
| 1) Run `npm init gatsby -- -y sheetjs-gatsby` to create the template site. | ||||
| 0) Disable GatsbyJS telemetry: | ||||
| 
 | ||||
| ```bash | ||||
| npx gatsby telemetry --disable | ||||
| ``` | ||||
| 
 | ||||
| 1) Create a template site: | ||||
| 
 | ||||
| ```bash | ||||
| npm init gatsby -- -y sheetjs-gatsby | ||||
| ``` | ||||
| 
 | ||||
| 2) Follow the on-screen instructions for starting the local development server: | ||||
| 
 | ||||
| @ -157,7 +257,7 @@ Stop and restart the development server process (`npm run develop`). | ||||
| 
 | ||||
| ### GraphiQL test | ||||
| 
 | ||||
| 7) Open the GraphiQL editor at `http://localhost:8000/___graphql`. | ||||
| 7) Open the GraphiQL editor at `http://localhost:8000/___graphql` | ||||
| 
 | ||||
| There is an editor in the left pane.  Paste the following query into the editor: | ||||
| 
 | ||||
| @ -203,8 +303,8 @@ const PageComponent = ({data}) => { | ||||
| export default PageComponent; | ||||
| ``` | ||||
| 
 | ||||
| After saving the file, access `http://localhost:8000/pres`.  The displayed JSON | ||||
| is the data that the component receives: | ||||
| After saving the file, access `http://localhost:8000/pres` in the browser. The | ||||
| displayed JSON is the data that the component receives: | ||||
| 
 | ||||
| ```js | ||||
| { | ||||
| @ -301,3 +401,9 @@ There will be a HTML row: | ||||
| ```html title="public/pres/index.html" | ||||
| <tr><td>SheetJS Dev</td><td>47</td></tr> | ||||
| ``` | ||||
| 
 | ||||
| [^1]: The package is available as [`gatsby-transformer-excel` on the public NPM registry](https://www.npmjs.com/package/gatsby-transformer-excel). It is also listed on the [GatsbyJS plugin library](https://www.gatsbyjs.com/plugins/gatsby-transformer-excel/). | ||||
| [^2]: See [the `gatsby-source-filesystem` plugin](https://www.gatsbyjs.com/plugins/gatsby-source-filesystem/) in the GatsbyJS documentation | ||||
| [^3]: See [`read` in "Reading Files"](/docs/api/parse-options) | ||||
| [^4]: See ["Workbook Object"](/docs/csf/book) for more details on the SheetJS workbook object. | ||||
| [^5]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output) | ||||
| @ -25,14 +25,15 @@ plugin and compare a few different data loading strategies. | ||||
| The ["Complete Demo"](#complete-demo) section creates a complete website powered | ||||
| by a XLSX spreadsheet. | ||||
| 
 | ||||
| :::tip pass | ||||
| :::info pass | ||||
| 
 | ||||
| This demo covers use cases where data is available at build time. This flow is | ||||
| suitable for end of week or end of month (EOM) reports published in HTML tables | ||||
| suitable for end of week or end of month (EOM) reports published in HTML tables. | ||||
| 
 | ||||
| For processing user-submitted files in the browser, the | ||||
| ["Bundlers" demo](/docs/demos/frontend/bundler#vite) shows client-side bundling | ||||
| of the SheetJS library. | ||||
| of the SheetJS library. The ["ReactJS" demo](/docs/demos/frontend/react) shows | ||||
| example sites using ViteJS with the ReactJS starter. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
|  | ||||
| @ -7,10 +7,15 @@ pagination_next: demos/mobile/index | ||||
| import current from '/version.js'; | ||||
| import CodeBlock from '@theme/CodeBlock'; | ||||
| 
 | ||||
| Eleventy is a static site generator. | ||||
| [Eleventy](https://www.11ty.dev/docs) is a telemetry-free static site generator. | ||||
| The data pipeline can be augmented with custom data file parsers. | ||||
| 
 | ||||
| The [NodeJS module](/docs/getting-started/installation/nodejs) can be loaded in | ||||
| `.eleventy.js` and used in custom data file format parsers. | ||||
| [SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing | ||||
| data from spreadsheets. | ||||
| 
 | ||||
| This demo uses Eleventy and SheetJS to pull data from a spreadsheet and display | ||||
| the content in a page. We'll explore how to load SheetJS libraries in a custom | ||||
| data file format parser and generate arrays of objects for use in pages. | ||||
| 
 | ||||
| The following diagram depicts the workbook waltz: | ||||
| 
 | ||||
| @ -27,19 +32,33 @@ flowchart LR | ||||
|   aoo --> |index.njk\ntemplate| html | ||||
| ``` | ||||
| 
 | ||||
| :::tip No Telemetry | ||||
| 
 | ||||
| The author has publicly stated that Eleventy does not embed any telemetry or | ||||
| data collection.[^1] | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ## Integration Details | ||||
| 
 | ||||
| The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be | ||||
| loaded in `.eleventy.js` and used in custom data file format parsers. | ||||
| 
 | ||||
| ### Data File Parser | ||||
| 
 | ||||
| Custom data file parsers must be registered in `.eleventy.js` | ||||
| 
 | ||||
| `addDataExtension` accepts a list of file extensions and a parser object. | ||||
| The Eleventy config `addDataExtension` method[^2] accepts a list of file | ||||
| extensions and a parser configuration object. | ||||
| 
 | ||||
| The parser object must include the options `read: true` and `encoding: null` . | ||||
| Eleventy will read files and pass raw `Buffer` objects to the parser callback. | ||||
| 
 | ||||
| The `parser` callback can parse the data with `XLSX.read`. In this demo, the | ||||
| parser will generate an array of row objects using `XLSX.utils.sheet_to_json`: | ||||
| The `parser` callback can parse the raw `Buffer` data with the SheetJS `read` | ||||
| method[^3]. The method returns a workbook object[^4]. | ||||
| 
 | ||||
| In this example, the parser will use the SheetJS `sheet_to_json` method[^5] to | ||||
| generate an array of objects from the data in the first worksheet: | ||||
| 
 | ||||
| ```js title=".eleventy.js" | ||||
| const XLSX = require("xlsx"); | ||||
| @ -91,7 +110,7 @@ using the variable `pres` in a template: | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was tested on 2023 May 07 using Eleventy `2.0.1` | ||||
| This demo was tested on 2023 October 08 using Eleventy `2.0.1` | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| @ -179,3 +198,9 @@ npx http-server _site | ||||
| Open a web browser and access the displayed URL ( `http://localhost:8080` ). | ||||
| View the page source and confirm that no JS was added to the page.  It only | ||||
| contains the content from the file in an HTML table. | ||||
| 
 | ||||
| [^1]: See [the "Telemetry" section](https://www.zachleat.com/web/site-generator-review/#telemetry) of the site generator review from the author of Eleventy. When this page was last checked, the author proudly asserted that Eleventy had "No known Telemetry or data collection". | ||||
| [^2]: See ["Custom Data File Formats"](https://www.11ty.dev/docs/data-custom/) in the Eleventy documentation. | ||||
| [^3]: See [`read` in "Reading Files"](/docs/api/parse-options) | ||||
| [^4]: See ["Workbook Object"](/docs/csf/book) for more details on the SheetJS workbook object. | ||||
| [^5]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output) | ||||
| @ -37,13 +37,11 @@ the browser refreshes to show the new content. | ||||
| 
 | ||||
| :::note Recommendation | ||||
| 
 | ||||
| It is strongly recommended to use a framework that provides an official plugin | ||||
| for working with SheetJS. | ||||
| It is strongly recommended to use a telemetry-free framework that provides an | ||||
| official plugin for working with SheetJS. | ||||
| 
 | ||||
| Lume is a great choice for lightweight sites. | ||||
| 
 | ||||
| GatsbyJS is excellent for teams well-versed in the ReactJS framework. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ### Official | ||||
|  | ||||
| @ -145,10 +145,6 @@ If the file does not exist, servers will send a 404 response that may include a | ||||
| friendly HTML page. Without checking the response code, the integration will try | ||||
| to read the 404 page and fail since the HTML typically has no TABLE elements. | ||||
| 
 | ||||
| When building a project with CRA or other templates, spreadsheets must be placed | ||||
| in the `public` folder. That folder is served by the dev server and copied in | ||||
| the build process. | ||||
| 
 | ||||
| Integration code should defend against network issues by checking status code. | ||||
| For example, when using `fetch`: | ||||
| 
 | ||||
| @ -161,6 +157,14 @@ async function fetch_workbook_and_error_on_404(url) { | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| :::note pass | ||||
| 
 | ||||
| When building a project with `create-react-app` or other templates, spreadsheets | ||||
| must be placed in the `public` folder. That folder is typically served by the | ||||
| dev server and copied to the production site in the build process. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| #### Cloudflare Worker "Error: Script startup exceeded CPU time limit." | ||||
| 
 | ||||
| This may show up in projects with many dependencies. The official workaround is | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user