From 35a641d0f793d3812fc08932db5156e226117f2f Mon Sep 17 00:00:00 2001 From: Elisha Nuchi <31550519+enuchi@users.noreply.github.com> Date: Sat, 15 Aug 2020 13:09:04 -0400 Subject: [PATCH] v2 Enable local development (#52) - Create local dev environment - Add gas-client and google-apps-script-webpack-dev-server packages for development - Add dev/ wrapper app - Update webpack config to support development environment - Redesigned READMEs - Support typescript - Update npm scripts - Organize and update .gitignore file - Add .vscode settings for clasp files - Remove tracking for files in dist/ --- .claspignore | 2 +- .eslintrc.json | 13 +- .gitignore | 15 +- .vscode/settings.json | 13 + LICENSE | 26 +- README.md | 406 +- dev/.babelrc | 9 + dev/.eslintrc.json | 44 + dev/README.md | 15 + dev/index.html | 19 + dev/index.js | 66 + dist/appsscript.json | 10 - dist/code.js | 137 - dist/dialog-demo-bootstrap.html | 18 - dist/dialog-demo.html | 16 - dist/sidebar-about-page.html | 12 - package-lock.json | 5701 +++++++++++++---- package.json | 75 +- src/client/.eslintrc.json | 2 +- src/client/README.md | 31 + .../components/FormInput.tsx | 57 + .../components/SheetEditor.jsx | 53 +- .../dialog-demo/components/SheetEditor.jsx | 19 +- .../sidebar-about-page/components/About.jsx | 13 +- src/client/utils/server.js | 34 +- src/server/.eslintrc.json | 5 + src/server/README.md | 19 + src/server/ui.js | 7 +- tsconfig.json | 7 + webpack.config.js | 290 +- 30 files changed, 5392 insertions(+), 1742 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 dev/.babelrc create mode 100644 dev/.eslintrc.json create mode 100644 dev/README.md create mode 100644 dev/index.html create mode 100644 dev/index.js delete mode 100644 dist/appsscript.json delete mode 100644 dist/code.js delete mode 100644 dist/dialog-demo-bootstrap.html delete mode 100644 dist/dialog-demo.html delete mode 100644 dist/sidebar-about-page.html create mode 100644 src/client/README.md create mode 100644 src/client/dialog-demo-bootstrap/components/FormInput.tsx create mode 100644 src/server/README.md create mode 100644 tsconfig.json diff --git a/.claspignore b/.claspignore index 82df346..a9b203a 100644 --- a/.claspignore +++ b/.claspignore @@ -1 +1 @@ -main.js \ No newline at end of file +main.js diff --git a/.eslintrc.json b/.eslintrc.json index 8dafe48..8208183 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,3 +1,14 @@ { - "parser": "babel-eslint" + "root": true, + "parser": "babel-eslint", + "extends": ["airbnb-base", "plugin:prettier/recommended"], + "plugins": ["prettier"], + "rules": { + "prettier/prettier": "error", + "camelcase": "warn", + "import/prefer-default-export": "warn", + "import/no-extraneous-dependencies": "warn", + "prefer-object-spread": "warn", + "spaced-comment":"off" + } } diff --git a/.gitignore b/.gitignore index 14e82a5..0b3aece 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,16 @@ +# npm node_modules + +# clasp files .clasp* -creds.json \ No newline at end of file +creds.json + +# certs +*.pem +certs/ + +# build +dist/ + +# mac +.DS_Store diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f323851 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "json.schemas": [{ + "fileMatch": [ + "appsscript.json" + ], + "url": "http://json.schemastore.org/appsscript" + }, { + "fileMatch": [ + ".clasp.json" + ], + "url": "http://json.schemastore.org/clasp" + }] +} diff --git a/LICENSE b/LICENSE index 7621b34..4a01b34 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,30 @@ MIT License -Copyright (c) 2018 Elisha Nuchi +Copyright (c) 2020 Elisha Nuchi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Sections adapted from github.com/labnol/apps-script-starter + +MIT License + +Copyright (c) 2018 Amit Agarwal Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 51962ac..a53bf44 100644 --- a/README.md +++ b/README.md @@ -1,189 +1,299 @@ - +

+ + React & Google Apps Script logos +

+
-# React & Google Apps Script -_Use this project as your boilerplate for React apps inside Google Sheets, Docs and Forms dialogs._ +[![Status](https://img.shields.io/badge/status-active-success.svg?color=46963a&style=flat-square)]() +[![GitHub Issues](https://img.shields.io/github/issues/enuchi/React-Google-Apps-Script.svg?color=lightblue&style=flat-square)](https://github.com/enuchi/React-Google-Apps-Script/issues) +[![GitHub Pull Requests](https://img.shields.io/github/issues-pr/enuchi/React-Google-Apps-Script.svg?color=blue&style=flat-square)](https://github.com/enuchi/React-Google-Apps-Script/pulls) +[![License](https://img.shields.io/github/license/enuchi/React-Google-Apps-Script?color=pink&style=flat-square)](/LICENSE) -This project uses labnol's excellent [apps-script-starter](https://github.com/labnol/apps-script-starter) as a starting point for developing server-side projects, and adds support for building React apps inside Google Sheets, Forms, and Docs dialogs. Simply clone this project and modify the source code to get started developing React apps with Google Apps Script. +
-![Google Apps Script / React development](https://i.imgur.com/z99uP89.png 'Start a React project for Google Apps Script') -_The included demo React app for Google Sheets shows insertion, deletion and selection of sheets through the dialog window._ +

This is your boilerplate project for developing React apps inside Google Sheets, Docs, Forms and Slides projects. It's perfect for personal projects and for publishing complex add-ons in the G Suite Marketplace. +

-  +--- -## Installation +## 📝 Table of Contents -1. Clone the sample project and install dependencies: - ```bash - > git clone https://github.com/enuchi/React-Google-Apps-Script.git - > cd React-Google-Apps-Script - > npm install - ``` -2. Enable the Google Apps Script API for your account by visiting [script.google.com/home/usersettings](https://script.google.com/home/usersettings): +- [About](#about) +- [Install](#install) + - [Prerequisites](#prerequisites) + - [Getting started](#getting-started) +- [Deploy](#deploy) +- [[New!] Local Development](#local-development) +- [Usage](#usage) + - [The included sample app](#the-included-sample-app) + - [[New!] Typescript](#new-typescript) + - [Adding packages](#adding-packages) + - [Styles](#styles) + - [Modifying scopes](#modifying-scopes) + - [Calling server-side Google Apps Script functions](#calling-server-side-google-apps-script-functions) + - [Autocomplete](#Autocomplete) +- [Authors](#authors) +- [Acknowledgments](#acknowledgement) - +
-3. Log in to `clasp`: - ```bash - > npm run login - ``` - This will use `clasp` to manage your login credentials. `clasp` is [Google's tool](https://github.com/google/clasp) that helps you develop and manage Apps Script projects locally. -4. Run the setup command to generate a new Google Sheets spreadsheet and bound Google Scripts project for your React project: +## 🔎 About - ```bash - > npm run setup +[Google Apps Script](https://developers.google.com/apps-script/overview) is Google's Javascript-based development platform for building applications and add-ons for Google Sheets, Docs, Forms and other Google Apps. - Created new Google Sheet: https://drive.google.com/open?id=1lVQUPZ************************************* - Created new Google Sheets Add-on script: https://script.google.com/d/1K7MPtCH*************************************-**/edit - ``` +You can add custom [user interfaces inside dialog windows](https://developers.google.com/apps-script/guides/html), but the platform is designed for simple HTML pages built with [templates](https://developers.google.com/apps-script/guides/html/templates) and [jQuery](https://developers.google.com/apps-script/guides/html/best-practices#take_advantage_of_jquery). - This will use `clasp` to create the new files, and save the project's ID to the `.clasp.json` file in your root directory. If you don't want to create a new spreadsheet and script, and instead want to use this React project with an existing project, [see the section below](#Using-an-existing-Sheet). +However, using this repo, it's easy to run [React](https://reactjs.org/) apps inside these dialogs, and build everything from small projects to advanced add-ons that can be published on the G Suite Marketplace. - Okay, now you've created a new sheet and bound script project! (But they're still empty for now.) +

+ React & Google Apps Script +

-5. Deploy the app with the command below: - ```bash - > npm run deploy - ``` - This will build and push your code to your script project. Open the new spreadsheet in Google Sheets and your new React app will be available from the menu bar! +This repo is a boilerplate project that uses React and the same development tools that you use for building traditional websites, all inside Google Apps Script projects. -### Using an existing Sheet +See below how to get started! -If you want to use an existing google script for your project: +
+ +## 🚜 Install + +These instructions will get you set up with a copy of the React project code on your local machine. It will also get you logged in to `clasp` so you can manage script projects from the command line. + +See [deploy](#deploy) for notes on how to deploy the project and see it live in a Google Spreadsheet. + +### Prerequisites + +- Make sure you're running at least [Node.js](https://nodejs.org/en/download/) v10 and `npm` v6. + +- You'll need to enable the Google Apps Script API. You can do that by visiting [script.google.com/home/usersettings](https://script.google.com/home/usersettings). + +- [New!] To use live reload while developing, you'll need to serve your files locally using HTTPS. See [local development](#local-development) below for how to set up your local environment. + +### 🏁 Getting started + +**1.** First, let's clone the repo and install the dependencies. + +```bash +git clone https://github.com/enuchi/React-Google-Apps-Script.git +cd React-Google-Apps-Script +npm install +``` + + + +**2.** Next, we'll need to log in to [clasp](https://github.com/google/clasp), which lets us manage our Google Apps Script projects locally. + +```bash +npm run login +``` + + + +**3.** Now let's run the setup script to create a New spreadsheet and script project from the command line. + +```bash +npm run setup +``` + + + +Alternatively, you can use an existing Google Spreadsheet and Script file instead of creating a new one. + +
+ See instructions here for using an existing project. 1. Copy your existing script project's `scriptId`. You can find it by opening your spreadsheet, selecting **Tools > Script Editor** from the menubar, then **File > Project properties**. 2. Run the command below using your project's `scriptId`: ```bash - # If your scriptId is 1K7MPtCHkjasdf93238234asdKFDF3sa9 then run - > npm run setup:use-id 1K7MPtCHkjasdf93238234asdKFDF3sa9 + npm run setup:use-id your_script_id_here ``` - This command will add the existing project's `scriptId` to your`.clasp.json` file. (You can also just edit the `.clasp.json` file directly. Make sure not to remove `"rootDir": "dist"` from the `.clasp.json` file.) + This command will add the existing project's `scriptId` to your`.clasp.json` file. See [here](https://github.com/google/clasp#setting) for working with `clasp`. -### Making changes to the code + -Modify the server-side and client-side source code in the `src` folder. [Add any additional scopes](https://developers.google.com/apps-script/concepts/scopes) to `appsscript.json` as needed. When you're ready, build the app and deploy! You can run `npm run deploy` to build and deploy, or `npm run build` just to build the bundled files in the `./dist` directory. +
-## The sample app +Next, let's deploy the app so we can see it live in Google Spreadsheets. -The included sample app allows inserting/activating/deleting sheets through a simple HTML dialog, built with React. Two versions of the same app are provided with different styling: the first version uses vanilla React, and the second uses the popular bootstrap library (in this case, it uses [`react-bootstrap`](https://react-bootstrap.github.io/)). Access the dialogs through the new menu item that appears. You may need to refresh the spreadsheet and approve the app's permissions the first time you use it. +
-## Features +## 🚀 Deploy -- Includes popular `eslint` and `prettier` configs for clean, standardized code. -- Supports importing CSS from another file: - ```js - import './styles.css'; - ``` -- This project uses promises to call and handle responses from the server, instead of using `google.script.run`: - ```js - // Google's documentation wants you to do this. Boo. - google.script.run - .withSuccessHandler(response => doSomething(response)) - .withFailureHandler(err => handleError(err)) - .addSheet(sheetTitle); +Run the deploy command. You may be prompted to update your manifest file. Type 'yes'. - // Poof! With a little magic we can now do this: - import server from '../server'; +```bash +npm run deploy +``` - // We now have access to all our server functions, which return promises! - server.addSheet(sheetTitle) - .then(response => doSomething(response)) - .catch(err => handleError(err)); +The deploy command will build all necessary files using production settings, including all server code (Google Apps Script code), client code (React bundle), and config files. All bundled files will be outputted to the `dist/` folder, then pushed to the Google Apps Script project. - // Or we can use async/await. This is the same thing as above: - async () => { - try { - const response = await server.addSheet(sheetTitle); - doSomething(response); - } catch (err) { - handleError(err) - } +Now open Google Sheets and navigate to your new spreadsheet (e.g. the file "My React Project"). Make sure to refresh the page if you already had it open. You will now see a new menu item appear containing your app! + + + +
+ +## 🎈 [NEW!] Local Development + +We can develop our client-side React apps locally, and see our changes directly inside our Google Spreadsheet dialog window. + + + +There are two steps to getting started: installing a certificate (first time only), and running the start command. + +1. Generating a certificate for local development + + Install the mkcert package: + + ```bash + # mac: + $ brew install mkcert + + # windows: + $ choco install mkcert + ``` + + [More install options here.](https://github.com/FiloSottile/mkcert#installation) + + Then run the mkcert install script: + + ```bash + $ mkcert -install + ``` + + Create the certs in your repo: + + ``` + npm run setup:https + ``` + +2. Now you're ready to start: + ```bash + npm run start + ``` + +The start command will create and deploy a development build, and serve your local files. + + + +After running the start command, navigate to your spreadsheet and open one of the menu items. It should now be serving your local files. When you make and save changes to your React app, your app will reload instantly within the Google Spreadsheet, and have access to any server-side functions! + + + +
+ +## ⛏️ Usage + +### The included sample app + +The included sample app allows inserting/activating/deleting sheets through a simple HTML dialog, built with React. This simple app demonstrates how a React app can interact with the underlying Spreadsheet using Google Apps Script functions. + +The included sample app has three menu items for loading pages in various dialogs and sidebars. + +Two versions of the same app are provided with different styling: the first version uses vanilla React, and the second uses the popular bootstrap library (in this case, it uses [`react-bootstrap`](https://react-bootstrap.github.io/)). The bootstrap example also contains an example of a page built with typescript (see below) + +A third app just demonstrates how to load a sidebar dialog. + +Access the dialogs through the new menu item that appears. You may need to refresh the spreadsheet and approve the app's permissions the first time you use it. + +### [New!] Typescript + +This project now supports typescript! + +To use, simply use a typescript extension in either the client code (.ts/.tsx) or the server code (.ts), and your typescript file will compile to the proper format. + +For client-side code, see [FormInput.tsx in the Bootstrap demo](./src/client/dialog-demo-bootstrap/components/FormInput.tsx) for an example file. Note that it is okay to have a mix of javascript and typescript, as seen in the Bootstrap demo. + +To use typescript in server code, just change the file extension to .ts. The server-side code already utilizes type definitions for Google Apps Script APIs. + +A basic typescript configuration is used here, because after code is transpiled from typescript to javascript it is once again transpiled to code that is compatible with Google Apps Script. However, if you want more control over your setup you can modify the included [tsconfig.json file](./tsconfig.json). + +### Adding packages + +You can add packages to your client-side React app. + +For instance, install `react-transition-group` from npm: + +```bash +npm install react-transition-group +``` + +Important: Since Google Apps Scripts projects don't let you easily reference external files, this project will bundle an entire app into one HTML file. This can result in large files if you are importing large packages. To help split up the files, you can grab a CDN url for your package and declare it in the [webpack file, here](./webpack.config.js#L129). If set up properly, this will add a script tag that will load packages from a CDN, reducing your bundle size. + +### Styles + +By default this project supports global CSS stylesheets. Make sure to import your stylesheet in your entrypoint file [index.js](./src/client/dialog-demo/index.js): + +```javascript +import './styles.css'; +``` + +Many external component libraries require a css stylesheet in order to work properly. You can import stylesheets in the HTML template, [as shown here with the Bootstrap stylesheet](./src/client/dialog-demo-bootstrap/index.html). + +The webpack.config.js file can also be modified to support scss and other style libraries. + +### Modifying scopes + +The included app only requires access to Google Spreadsheets and to loading dialog windows. If you make changes to the app's requirements, for instance, if you modify this project to work with Google Forms or Docs, make sure to edit the oauthScopes in the [appscript.json file](./appsscript.json). + +See https://developers.google.com/apps-script/manifest for information on the `appsscript.json` structure. + +### Calling server-side Google Apps Script functions + +This project uses the [gas-client](https://github.com/enuchi/gas-client) package to more easily call server-side functions using promises. + +```js +// Google's documentation wants you to do this. Boo. +google.script.run + .withSuccessHandler(response => doSomething(response)) + .withFailureHandler(err => handleError(err)) + .addSheet(sheetTitle); + +// Poof! With a little magic we can now do this: +import Server from 'gas-client'; +const { serverFunctions } = new Server(); + +// We now have access to all our server functions, which return promises! +serverFunctions + .addSheet(sheetTitle) + .then(response => doSomething(response)) + .catch(err => handleError(err)); + +// Or we can equally use async/await style: +async () => { + try { + const response = await serverFunctions.addSheet(sheetTitle); + doSomething(response); + } catch (err) { + handleError(err); } - - ``` - Now we can use familiar Promises in our client-side code and have easy access to all server functions. See [the code](./src/client/utils/server.js) for the implementation details. - -- This project includes support for autocompletion and complete type definitions for all Google Apps Script methods. - - ![autocomplete support](https://i.imgur.com/W0Ks6Wj.gif "autocomplete") - -- All available methods from the Google Apps Script API are shown with full definitions and links to the official documentation, plus information on argument and return type - - -## Extending this app -### Adding new libraries and packages -To add new client-side libraries for your React app: -1. Install from npm, e.g. `npm install react-transition-group` -2. Grap a CDN url and declare it in the [webpack file, here](./webpack.config.js#L129). - -Longer explanation: - -Google Apps Script requires all HTML to be in a single file that is loaded into the dialog. Therefore, webpack has been configured to inline all generated client JS code into a single HTML file (in this case [main.html](./dist/main.html) and [about.html](./dist/about.html)). Inlining all code into a single file can result in large output files, which take longer to load, and can also sometimes cause the Google Apps Script editor to crash when you open it. So to reduce bundle size this project takes advantage of [dynamic-cdn-webpack-plugin](https://github.com/mastilver/dynamic-cdn-webpack-plugin) to automatically load popular libraries found in your app, such as `react` and `react-dom`, from a CDN. It doesn't know about all libraries (only [these](https://github.com/mastilver/module-to-cdn/blob/master/modules.json)), so if you've installed new packages, especially large packages, you should add a CDN url to the [webpack file](./webpack.config.js#L129) to reduce bundle size. You will need to know your package's global variable. See the examples provided, and also see [here](https://webpack.js.org/configuration/externals/#externals) for more info on how this works. - -### Expose all public functions -Make sure to expose all public functions, including `onOpen` and any functions you are calling from the client. Example below shows assignment to `global` object: -```js -const onOpen = () => { - SpreadsheetApp.getUi() // Or DocumentApp or FormApp. - .createMenu('Dialog') - .addItem('Add sheets', 'openDialog') - .addToUi(); -} - -global.onOpen = onOpen -``` - -## Multiple dialogs - -This project now supports multiple dialogs and sidebars. See the `server` code at [src/server/ui.js](./src/server/ui.js) for a 'main.html' dialog and an 'about.html' sidebar: - -```js -// ./src/server/ui.js - -export const onOpen = () => { - SpreadsheetApp.getUi() - .createMenu('My Sample React Project') // edit me! - .addItem('Sheet Name Editor', 'openDialog') - .addItem('About me', 'openAboutSidebar') - .addToUi(); -}; - -export const openDialog = () => { - const html = HtmlService.createHtmlOutputFromFile('main') - .setWidth(400) - .setHeight(600); - SpreadsheetApp.getUi().showModalDialog(html, 'Sheet Editor'); -}; - -export const openAboutSidebar = () => { - const html = HtmlService.createHtmlOutputFromFile('about'); - SpreadsheetApp.getUi().showSidebar(html); }; ``` -And here is the configuration in webpack that creates multiple html files. You will need to edit this if you want to add more dialog html files: +In development, `gas-client` will interact with [the custom Webpack Dev Server package](https://github.com/enuchi/Google-Apps-Script-Webpack-Dev-Server) which allows us to run our app within the dialog window and still interact with Google Apps Script functions. -```js -// ./webpack.config.js +### Autocomplete -// Client entrypoints: -const clientEntrypoints = [ - { - name: 'CLIENT - main dialog', - entry: './src/client/MainDialog.jsx', - filename: 'main.html', - }, - { - name: 'CLIENT - about sidebar', - entry: './src/client/AboutDialog.jsx', - filename: 'about.html', - }, -]; -``` +This project includes support for autocompletion and complete type definitions for Google Apps Script methods. -## Suggestions +![autocomplete support](https://i.imgur.com/E7FLeTX.gif 'autocomplete') -Pull requests welcome! +All available methods from the Google Apps Script API are shown with full definitions and links to the official documentation, plus information on argument, return type and sample code. + +
+ +## ✍️ Authors + +- [@enuchi](https://github.com/enuchi) - Creator and maintainer + +See the list of [contributors](https://github.com/enuchi/React-Google-Apps-Script/contributors) who participated in this project. + +
+ +## 🎉 Acknowledgements + +Part of this project has been adapted from [apps-script-starter](https://github.com/labnol/apps-script-starter), a great starter project for server-side projects. diff --git a/dev/.babelrc b/dev/.babelrc new file mode 100644 index 0000000..749a4ef --- /dev/null +++ b/dev/.babelrc @@ -0,0 +1,9 @@ +{ + "presets": [ + "@babel/react" + ], + "plugins": [ + "@babel/plugin-proposal-object-rest-spread", + "@babel/plugin-proposal-class-properties" + ] +} diff --git a/dev/.eslintrc.json b/dev/.eslintrc.json new file mode 100644 index 0000000..6bcea06 --- /dev/null +++ b/dev/.eslintrc.json @@ -0,0 +1,44 @@ +{ + "root": true, + "parser": "babel-eslint", + "extends": [ + "airbnb-base", + "plugin:prettier/recommended", + "plugin:react/recommended" + ], + "plugins": ["babel", "react", "prettier"], + "env": { + "browser": true, + "es6": true + }, + "globals": { + "google": false, + "alert": false, + "css": true + }, + "parserOptions": { + "ecmaVersion": 9, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true + } + }, + "rules": { + "prettier/prettier": "error", + "react/prop-types": "warn", + "camelcase": "warn", + "import/prefer-default-export": "warn", + "import/no-extraneous-dependencies": "warn", + "prefer-object-spread": "warn" + }, + "settings": { + "react": { + "version": "detect" + }, + "import/resolver": { + "node": { + "extensions": [".js", ".jsx"] + } + } + } +} diff --git a/dev/README.md b/dev/README.md new file mode 100644 index 0000000..7686a6d --- /dev/null +++ b/dev/README.md @@ -0,0 +1,15 @@ +# Development App Wrapper + +This directory contains the app needed to run development mode. + +It utilizes special Webpack configurations as well as two packages, [gas-client](https://github.com/enuchi/gas-client) and [Webpack Dev Server for Google Apps Script](https://github.com/enuchi/Google-Apps-Script-Webpack-Dev-Server), in order to achieve hot reloading inside of a dialog window. + +## How it works + +Running `npm run start` will build and deploy the development app, and then serve files locally. + +The simple React app in this directory, found at [index.js](./index.js), is designed to only be used with development builds. It loads an iframe with the source pointing to `https://localhost:${PORT}/gas/${FILENAME}-impl.html`. During the build step, we will replace `FILENAME` with the appropriate name of the HTML file to load. + +As an example, during the development build, the file `dialog-demo-bootstrap.html` will be generated from the app in this directory, using `dialog-demo-bootstrap` as the `FILENAME` for the iframe source of this page. Opening the menu items in the Google Spreadsheet will load this app, and webpack's devServer settings will serve `https://localhost:3000/gas/dialog-demo-bootstrap-impl.html` within the iframe, using the customized Webpack Dev Server build. + +The customized Google Apps Script Webpack Dev Server acts very similarly to Webpack Dev Server's iframe mode, but is able to pass requests to Google Apps Script server functions back and forth between all the iframes being used in development. diff --git a/dev/index.html b/dev/index.html new file mode 100644 index 0000000..64900a9 --- /dev/null +++ b/dev/index.html @@ -0,0 +1,19 @@ + + + + + + + + +
+ + + diff --git a/dev/index.js b/dev/index.js new file mode 100644 index 0000000..3f1041e --- /dev/null +++ b/dev/index.js @@ -0,0 +1,66 @@ +import React, { useEffect } from 'react'; +import ReactDOM from 'react-dom'; +import server from '../src/client/utils/server'; + +const { serverFunctions } = server; + +const { FILENAME, PORT } = process.env; + +const DevServer = () => { + const iframe = React.useRef(null); + useEffect(() => { + const handleRequest = event => { + const request = event.data; + const { type, functionName, id, args } = request; + + if (type !== 'REQUEST') return; + + serverFunctions[functionName](...args) + .then(response => { + iframe.current.contentWindow.postMessage( + { type: 'RESPONSE', id, status: 'SUCCESS', response }, + `https://localhost:${PORT}` + ); + }) + .catch(err => { + iframe.current.contentWindow.postMessage( + { + type: 'RESPONSE', + id, + status: 'ERROR', + response: err, + }, + `https://localhost:${PORT}` + ); + }); + }; + + window.addEventListener('message', handleRequest, false); + }, []); + + return ( +
+