Google Sheets demo refresh

This commit is contained in:
SheetJS 2024-06-08 23:40:45 -04:00
parent a5e01f9476
commit 55ce74bf8c
15 changed files with 836 additions and 420 deletions

@ -81,6 +81,12 @@ do not work in MDX v2. Instead, string literals and concatenation must be used:
<a href={"Foo" + current + ""}>{"Foo" + current + ""}</a>
```
**Tables**
MDX inconsistently requires different indentation levels for `TD` / `TH`, `TR`,
`THEAD` / `TBODY` / `TFOOT`, and `TABLE` tags. Unconventional indentation is
intentional.
</details>

@ -72,7 +72,11 @@ The script https://docs.sheetjs.com/dojo/dojo.js was fetched from the official
:::note Tested Deployments
The demos were last tested on 2023-12-04.
This demo was tested in the following environments:
| Platform | Date |
|:-------------|:-----------|
| Chromium 125 | 2024-06-08 |
Demos exclusively using Dojo Core were tested using Dojo Toolkit `1.17.3`.

@ -34,8 +34,8 @@ This demo was tested in the following environments:
| Version | Date |
|:---------|:-----------|
| `2.10.3` | 2023-12-04 |
| `1.12.3` | 2023-12-04 |
| `2.12.0` | 2024-06-08 |
| `1.12.4` | 2024-06-08 |
:::
@ -135,7 +135,7 @@ main `index.html` page should load the `index.js` script:
<body>
<h3>SheetJS <span id="vers"></span> export demo</h3>
<button id="xport">Click to Export!</button>
<script src="index.js"></script>
<script src="index.js" type="module"></script>
<body>
```
@ -208,7 +208,7 @@ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
3) Run the ParcelJS development server:
```bash
npx -y parcel@2.10.3 index.html
npx -y parcel index.html
```
The process will print a URL:
@ -231,7 +231,7 @@ a web browser and click the "Click to Export!" button to generate a file.
6) Build the production site:
```bash
npx -y parcel@2.10.0 build index.html
npx -y parcel build index.html
```
The production site will be stored in the `dist` folder

File diff suppressed because it is too large Load Diff

@ -32,7 +32,12 @@ flowchart LR
:::note Tested Deployments
This demo was last tested by SheetJS users on 2024-04-25 in Maple 2024.
This demo was tested by SheetJS users in the following deployments:
| Architecture | Version | Date |
|:-------------|:--------|:-----------|
| `darwin-x64` | 2024 | 2024-04-25 |
| `win10-x64` | 2024 | 2024-04-25 |
:::

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

@ -0,0 +1,39 @@
import { google } from "googleapis";
import { set_fs, writeFile, utils } from 'xlsx';
import * as fs from 'fs';
set_fs(fs);
/* Change this import statement to point to the credentials JSON file */
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
/* Change this to the spreadsheet ID */
const id = "SOME-SPREADSHEETJS-ID";
/* connect to google services */
const jwt = new google.auth.JWT({
email: creds.client_email,
key: creds.private_key,
scopes: [
'https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/drive.file',
]
});
const sheets = google.sheets({ version: "v4", auth: jwt });
/* get existing sheets */
const wsheet = await sheets.spreadsheets.get({spreadsheetId: id});
/* create a workbook */
const wb = utils.book_new();
for(let sheet of wsheet.data.sheets) {
const name = sheet.properties.title;
const res = await sheets.spreadsheets.values.get({
spreadsheetId: id,
range: `'${name}'`
});
utils.book_append_sheet(wb, utils.aoa_to_sheet(res.data.values), name);
}
writeFile(wb, "SheetJSExport.xlsb");

@ -0,0 +1,87 @@
import { google } from "googleapis";
/* Change this import statement to point to the credentials JSON file */
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
/* Change this to the primary account address, NOT THE SERVICE ACCOUNT */
const acct = "YOUR_ADDRESS@gmail.com";
/* connect to google services */
const jwt = new google.auth.JWT({
email: creds.client_email,
key: creds.private_key,
scopes: [
'https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/drive.file',
]
});
const sheets = google.sheets({ version: "v4", auth: jwt });
const drive = google.drive({version: "v3", auth: jwt });
/* create new google workbook */
const [id, sheet0id] = await (async() => {
const res = await sheets.spreadsheets.create({
requestBody: {
properties: {
title: "SheetJS Test"
}
}
});
const id = res.data.spreadsheetId;
const sheet0id = res.data.sheets[0].properties.sheetId;
return [id, sheet0id];
})();
console.log(`Created Google Workbook ${id}`);
/* create new google worksheet and delete initial sheet */
const [sheet1id, sheet2id] = await (async() => {
const res = await sheets.spreadsheets.batchUpdate({
spreadsheetId: id,
requestBody: { requests: [
/* add SheetJS1 */
{ addSheet: { properties: { title: "SheetJS1" } } },
/* add SheetJS2 */
{ addSheet: { properties: { title: "SheetJS2" } } },
/* remove default sheet */
{ deleteSheet: { sheetId: sheet0id } },
] }
});
console.log(`Created Google Worksheets "SheetJS1" and "SheetJS2"`);
return res.data.replies.slice(0,2).map(r => r.addSheet.properties.sheetId);
})();
await sheets.spreadsheets.values.update({
spreadsheetId: id,
range: "SheetJS1!A1",
valueInputOption: "USER_ENTERED",
resource: { values: [
["Sheet", "JS"],
[72, 62]
]}
});
await sheets.spreadsheets.values.update({
spreadsheetId: id,
range: "SheetJS2!A1",
valueInputOption: "USER_ENTERED",
resource: { values: [
["Area Code", "Part 1", "Part 2"],
[201, 867, 5309],
[281, 330, 8004],
]}
});
/* Share new Document with the primary account */
try {
await drive.permissions.create({
fileId: id,
fields: "id",
requestBody: {
type: "user",
role: "writer",
emailAddress: acct
}
});
console.log(`Shared ${id} with ${acct}`);
} catch(e) { console.log(e); }

@ -0,0 +1,87 @@
import { google } from "googleapis";
import { set_fs, readFile, utils } from 'xlsx';
import * as fs from 'fs';
set_fs(fs);
/* Change this import statement to point to the credentials JSON file */
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
/* Change this to the spreadsheet ID */
const id = "SOME-SPREADSHEETJS-ID";
/* connect to google services */
const jwt = new google.auth.JWT({
email: creds.client_email,
key: creds.private_key,
scopes: [
'https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/drive.file',
]
});
const sheets = google.sheets({ version: "v4", auth: jwt });
/* get existing sheets */
const wsheet = await sheets.spreadsheets.get({spreadsheetId: id});
/* remove all sheets after the first */
if(wsheet.data.sheets.length > 1) await sheets.spreadsheets.batchUpdate({
spreadsheetId: id,
requestBody: { requests:
wsheet.data.sheets.slice(1).map(s => ({
deleteSheet: {
sheetId: s.properties.sheetId
}
}))
}
});
/* read file */
const wb = readFile("pres.numbers");
/* rename first worksheet to avoid collisions */
const props0 = wsheet.data.sheets[0].properties;
if(wb.SheetNames.map(n => n.toLowerCase()).includes(props0.title.toLowerCase())) {
await sheets.spreadsheets.batchUpdate({
spreadsheetId: id,
requestBody: { requests: [{
updateSheetProperties: {
fields: "title",
properties: {
sheetId: props0.sheetId,
title: "thistitleisatleast33characterslong"
}
}
}]}
});
console.log(`renamed "${props0.title}" to "thistitleisatleast33characterslong"`);
}
/* add sheets from file */
for(let name of wb.SheetNames) {
const aoa = utils.sheet_to_json(wb.Sheets[name], {header:1});
await sheets.spreadsheets.batchUpdate({
spreadsheetId: id,
requestBody: { requests: [
/* add new sheet */
{ addSheet: { properties: { title: name } } },
] }
});
await sheets.spreadsheets.values.update({
spreadsheetId: id,
range: `'${name}'!A1`,
valueInputOption: "USER_ENTERED",
resource: { values: aoa }
});
console.log(`Created Google Worksheet "${name}"`);
}
/* remove first sheet */
const res = await sheets.spreadsheets.batchUpdate({
spreadsheetId: id,
requestBody: { requests: [
/* remove old first sheet */
{ deleteSheet: { sheetId: wsheet.data.sheets[0].properties.sheetId } }
] }
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

@ -0,0 +1,38 @@
import { google } from "googleapis";
import { read, utils } from 'xlsx';
/* Change this import statement to point to the credentials JSON file */
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
/* Change this to the spreadsheet ID */
const id = "SOME-SPREADSHEETJS-ID";
/* connect to google services */
const jwt = new google.auth.JWT({
email: creds.client_email,
key: creds.private_key,
scopes: [
'https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/drive.file',
]
});
const drive = google.drive({ version: "v3", auth: jwt });
/* get XLSX export */
const file = await drive.files.export({
mimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
fileId: id
});
const ab = await file.data.arrayBuffer();
/* parse with SheetJS */
const wb = read(ab);
/* print CSV data from each worksheet */
wb.SheetNames.forEach(n => {
console.log(`#### ${n}`);
console.log(utils.sheet_to_csv(wb.Sheets[n]));
console.log("");
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

93
tests/bundler-parcel.sh Executable file

@ -0,0 +1,93 @@
#!/bin/bash
# https://docs.sheetjs.com/docs/demos/frontend/bundler/parcel
cd /tmp
rm -rf sheetjs-parceljs
mkdir sheetjs-parceljs
cd sheetjs-parceljs
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz
cat >index.html <<EOF
<body>
<h3>SheetJS <span id="vers"></span> export demo</h3>
<button id="xport">Click to Export!</button>
<script src="index.js" type="module"></script>
<body>
EOF
cat >index.js <<EOF
// ESM-style import from "xlsx"
import { utils, version, writeFileXLSX } from 'xlsx';
document.getElementById("vers").innerText = version;
document.getElementById("xport").onclick = async() => {
/* fetch JSON data and parse */
const url = "https://docs.sheetjs.com/executive.json";
const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
/* sort by first presidential term */
prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
prez.sort((l,r) => l.start.localeCompare(r.start));
/* flatten objects */
const rows = prez.map(row => ({
name: row.name.first + " " + row.name.last,
birthday: row.bio.birthday
}));
/* generate worksheet and workbook */
const worksheet = utils.json_to_sheet(rows);
const workbook = utils.book_new();
utils.book_append_sheet(workbook, worksheet, "Dates");
/* fix headers */
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
/* calculate column width */
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];
/* create an XLSX file and try to save to Presidents.xlsx */
writeFileXLSX(workbook, "Presidents.xlsx");
};
EOF
cat >test.js <<EOF
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
app.use(express.static('./dist'));
app.listen(7262, async() => {
await new Promise((res,rej) => setTimeout(res, 1000));
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
await page.setViewport({width: 1920, height: 1080});
const client = await page.target().createCDPSession();
await client.send('Browser.setDownloadBehavior', {
behavior: 'allow',
downloadPath: require("path").resolve('./')
});
page.on('request', req => console.log(req.url()));
await page.goto('http://localhost:7262/');
await new Promise((res,rej) => setTimeout(res, 1000));
await page.click("#xport");
await new Promise((res,rej) => setTimeout(res, 1000));
await browser.close();
process.exit();
});
EOF
node -e 'var pjson = JSON.parse(fs.readFileSync("./package.json")); console.log(pjson); delete pjson.main; fs.writeFileSync("package.json", JSON.stringify(pjson))'
for n in 1.12.4 2.12.0; do
npx -y parcel build index.html
node test.js
npx -y xlsx-cli Presidents.xlsx | head -n 3
rm -f Presidents.xlsx
done