From 2a37a9ff46226ba9fb3dd07dc0169bb41eb75fb0 Mon Sep 17 00:00:00 2001 From: Boris Edigariani Date: Tue, 9 Jul 2024 13:04:30 +0400 Subject: [PATCH 01/31] Initial commit with boilerplate --- .clasp.json | 7 + .clasp.json.SAMPLE | 7 + .claspignore | 2 + .eslintignore | 2 + .eslintrc.json | 37 + .../workflows/integration-tests-basic.yaml | 43 + .github/workflows/lint.yaml | 18 + .gitignore | 18 + .nvmrc | 1 + .prettierrc | 4 + .stylelintrc.json | 17 + .vscode/launch.json | 21 + .vscode/settings.json | 13 + LICENSE | 21 + README.md | 194 + appsscript.json | 10 + dev/dev-server-wrapper.html | 82 + jest.config.js | 6 + package.json | 85 + postcss.config.cjs | 6 + scripts/generate-cert.ps1 | 40 + src/client/.eslintrc.json | 45 + src/client/README.md | 31 + src/client/dialog/components/Dialog.tsx | 12 + src/client/dialog/index.html | 33 + src/client/dialog/index.jsx | 6 + src/client/dialog/styles.css | 4 + src/client/sidebar/components/Sidebar.tsx | 96 + src/client/sidebar/hooks/useURLChange.ts | 36 + src/client/sidebar/index.html | 20 + src/client/sidebar/index.jsx | 6 + src/client/utils/serverFunctions.ts | 10 + src/server/.eslintrc.json | 36 + src/server/README.md | 19 + src/server/index.ts | 14 + src/server/sheets.ts | 31 + src/server/ui.js | 21 + src/utils/MermaidChart.js | 250 + src/utils/index.js | 255 + tailwind.config.js | 9 + tsconfig.json | 27 + tsconfig.vite.json | 11 + vite.config.ts | 198 + yarn.lock | 8395 +++++++++++++++++ 44 files changed, 10199 insertions(+) create mode 100644 .clasp.json create mode 100644 .clasp.json.SAMPLE create mode 100644 .claspignore create mode 100644 .eslintignore create mode 100644 .eslintrc.json create mode 100644 .github/workflows/integration-tests-basic.yaml create mode 100644 .github/workflows/lint.yaml create mode 100644 .gitignore create mode 100644 .nvmrc create mode 100644 .prettierrc create mode 100644 .stylelintrc.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 LICENSE create mode 100644 README.md create mode 100644 appsscript.json create mode 100644 dev/dev-server-wrapper.html create mode 100644 jest.config.js create mode 100644 package.json create mode 100644 postcss.config.cjs create mode 100644 scripts/generate-cert.ps1 create mode 100644 src/client/.eslintrc.json create mode 100644 src/client/README.md create mode 100644 src/client/dialog/components/Dialog.tsx create mode 100644 src/client/dialog/index.html create mode 100644 src/client/dialog/index.jsx create mode 100644 src/client/dialog/styles.css create mode 100644 src/client/sidebar/components/Sidebar.tsx create mode 100644 src/client/sidebar/hooks/useURLChange.ts create mode 100644 src/client/sidebar/index.html create mode 100644 src/client/sidebar/index.jsx create mode 100644 src/client/utils/serverFunctions.ts create mode 100644 src/server/.eslintrc.json create mode 100644 src/server/README.md create mode 100644 src/server/index.ts create mode 100644 src/server/sheets.ts create mode 100644 src/server/ui.js create mode 100644 src/utils/MermaidChart.js create mode 100644 src/utils/index.js create mode 100644 tailwind.config.js create mode 100644 tsconfig.json create mode 100644 tsconfig.vite.json create mode 100644 vite.config.ts create mode 100644 yarn.lock diff --git a/.clasp.json b/.clasp.json new file mode 100644 index 0000000..93e6d50 --- /dev/null +++ b/.clasp.json @@ -0,0 +1,7 @@ +{ + "rootDir": "dist", + "scriptId": "1EBKJNiPs_XxntcBHxMKJgfBr7089AHhT3boz7_Lkc6AD0hjsQavkIjxz", + "parentId": [ + "1UAVn7sP1pTutyOM2i95jxQz6sjVr2ZrxhG82uLIdd70" + ] +} \ No newline at end of file diff --git a/.clasp.json.SAMPLE b/.clasp.json.SAMPLE new file mode 100644 index 0000000..69c48bb --- /dev/null +++ b/.clasp.json.SAMPLE @@ -0,0 +1,7 @@ +{ + "rootDir": "dist", + "scriptId": "...add scriptId here...", + "parentId": [ + "...spreadsheet/doc url ID here..." + ] +} diff --git a/.claspignore b/.claspignore new file mode 100644 index 0000000..0c9b48f --- /dev/null +++ b/.claspignore @@ -0,0 +1,2 @@ +main.js +*-impl.html diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..246d599 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +/dist +/node_modules \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..5bff5f7 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,37 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "extends": [ + "airbnb-base", + "plugin:prettier/recommended", + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "plugins": ["prettier", "googleappsscript"], + "env": { + "googleappsscript/googleappsscript": true + }, + "rules": { + "prettier/prettier": "error", + "camelcase": "warn", + "import/prefer-default-export": "warn", + "import/no-extraneous-dependencies": "warn", + "prefer-object-spread": "warn", + "import/extensions": [ + "error", + "ignorePackages", + { + "js": "never", + "ts": "never" + } + ] + }, + "ignorePatterns": ["dist", ".eslintrc.json"], + "settings": { + "import/resolver": { + "node": { + "extensions": [".js", ".ts"] + } + } + } +} diff --git a/.github/workflows/integration-tests-basic.yaml b/.github/workflows/integration-tests-basic.yaml new file mode 100644 index 0000000..587bbfe --- /dev/null +++ b/.github/workflows/integration-tests-basic.yaml @@ -0,0 +1,43 @@ +name: Local integration tests - Basic Version + +on: + pull_request: + branches: [main] + +jobs: + basic-integration-test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [macos-12, macos-13, windows-2022] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + node-version: [18, 20] + timeout-minutes: 8 + steps: + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - name: Install packages + run: yarn install + - name: Allow running mkcert on Mac + run: sudo security authorizationdb write com.apple.trust-settings.admin allow + if: runner.os == 'MacOS' + - name: Install mkcert + run: brew install mkcert + if: runner.os == 'MacOS' + - name: Run mkcert setup [mkcert -install] + run: mkcert -install + if: runner.os == 'MacOS' + - name: Install https cert [yarn setup:https] + run: yarn setup:https + if: runner.os == 'MacOS' + - run: | + mkdir certs + .\scripts\generate-cert.ps1 + shell: pwsh + if: runner.os == 'Windows' + - name: Run integration tests + run: yarn test:integration + shell: bash \ No newline at end of file diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 0000000..00ba1e1 --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,18 @@ +name: Lint + +on: [push, pull_request] + +jobs: + lint: + runs-on: macos-13 + timeout-minutes: 8 + steps: + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Install packages + run: yarn install + - name: Run lint + run: yarn lint diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7c24f20 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# npm +node_modules + +# clasp files +creds.json + +# certs +*.pem +certs/ + +# build +dist/ + +# mac +.DS_Store + +#secret +.env \ No newline at end of file diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..53d838a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +lts/gallium diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..c1a6f66 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "es5" +} diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 0000000..2650b41 --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,17 @@ +{ + "rules": { + "at-rule-no-unknown": [ + true, + { + "ignoreAtRules": [ + "extends", + "apply", + "tailwind", + "components", + "utilities", + "screen" + ] + } + ] + } +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..aab0dea --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "Launch spreadsheet with debugger", + "trace": true, + "sourceMaps": true, + "pauseForSourceMap": false, + "skipFiles": ["**/node_modules/**", "!${workspaceFolder}/**"], + "webRoot": "${workspaceFolder}/src/client", + // Need random open port for logging into spreadsheets: + // https://github.com/microsoft/vscode-js-debug/issues/918 + "port": 12345, + } + ] +} 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 new file mode 100644 index 0000000..a61c6fa --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..836a1c8 --- /dev/null +++ b/README.md @@ -0,0 +1,194 @@ +## 🚜 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`, which lets you 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 Docs. + +### Prerequisites + +- Make sure you're running at least [Node.js](https://nodejs.org/en/download/) v18 and [yarn (classic)](https://classic.yarnpkg.com/lang/en/docs/install/). + +- 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). + +- To use live reload while developing, you'll need to serve your files locally using HTTPS. See [local development](#local-development) below for instructions on setting up your local environment. + +### 🏁 Getting started + +**1.** First, let's clone the repo and install the dependencies. This project is published as a public template, so you can also fork the repo or select "Use this template" in GitHub. + +```bash +git clone https://github.com/Mermaid-Chart/google-plugin.git +cd google-plugin +yarn 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 +yarn run login +``` + +**3.** Now let's run the setup script to create a New document and script project from the command line. + +```bash +yarn run setup +``` + +Alternatively, you can use an existing Google document and Script file instead of creating a new one. + +
+ See instructions here for using an existing project. + +You will need to update the `.clasp.json` file in the root of this project with the following three key/value pairs (see .clasp.json.SAMPLE for reference): + +```json +{ + "scriptId": "1PY037hPcy................................................", + "parentId": ["1Df30......................................."], + "rootDir": "./dist" +} +``` + +- `scriptId`: Your existing script project's `scriptId`. You can find it by opening your document, selecting **Tools > Script Editor** from the menubar, then **File > Project properties**, and it will be listed as "Script ID". + +- `parentId`: An array with a single string, the ID of the parent file (document, doc, etc.) that the script project is bound to. You can get this ID from the url, where the format is usually `https://docs.google.com/documents/d/{id}/edit`. This allows you to run `npm run open` and open your file directly from the command line. + +- `rootDir`: This should always be `"./dist"`, i.e. the local build folder that is used to store project files. + +
+ +
+ +## 🚀 Deploy + +Run the deploy command. You may be prompted to update your manifest file. Type 'yes'. + +```bash +yarn run deploy +``` + +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. + +Now open Google Docs and navigate to your new document (e.g. the file "My React Project"). You can also run `yarn run open`. Make sure to refresh the page if you already had it open. You will now see a new menu item appear containing your app! + +
+ +## 🎈 Local Development + +We can develop our client-side React apps locally, and see our changes directly inside our Google document 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: + + ``` + yarn run setup:https + ``` + +2. Now you're ready to start: + ```bash + yarn 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 document 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 Docs, and have access to any server-side functions! + +
+ +### Typescript + +This project is built mainly with typescript but also supports Javascript, and examples of both are included here, both in server-side and client-side (React) code. The included sample app has a typescript example using the Bootstrap component library. + +To use typescript, 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. + +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 that correctly transpiles 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`: + +```bash +yarn add 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. If you are importing large libraries this can result in a large file. To help reduce the size of these large HTML files, you can try to externalize packages by using a CDN to load packages. For packages that can be loaded through a CDN (usually they will have a UMD build), you can configure the externals and globals details in the [vite config file](./vite.config.ts). You will also need to include a script element in the head of the `index.html` file, loading the library from a CDN, and making sure it supports a UMD build, e.g. +``. + +If set up properly, this will load packages from the CDN in production and will reduce your overall bundle size. + +Make sure that you update the script tag with the same version of the package you are installing with yarn, so that you are using the same version in development and production. + +### 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). + +### Modifying scopes + +The included app only requires access to Google documents 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 client-side google.script.run utility requires calling server-side functions like this: +google.script.run + .withSuccessHandler((response) => doSomething(response)) + .withFailureHandler((err) => handleError(err)) + .addSheet(sheetTitle); + +// Using gas-client we can use more familiar promises style like 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 with async/await: +async () => { + try { + const response = await serverFunctions.addSheet(sheetTitle); + doSomething(response); + } catch (err) { + handleError(err); + } +}; +``` + +In development, `gas-client` will allow you to call server-side functions from your local environment. In production, it will use Google's underlying `google.script.run` utility. diff --git a/appsscript.json b/appsscript.json new file mode 100644 index 0000000..8f799f9 --- /dev/null +++ b/appsscript.json @@ -0,0 +1,10 @@ +{ + "timeZone": "America/New_York", + "dependencies": {}, + "exceptionLogging": "STACKDRIVER", + "oauthScopes": [ + "https://www.googleapis.com/auth/script.container.ui", + "https://www.googleapis.com/auth/spreadsheets" + ], + "runtimeVersion": "V8" +} diff --git a/dev/dev-server-wrapper.html b/dev/dev-server-wrapper.html new file mode 100644 index 0000000..c050c05 --- /dev/null +++ b/dev/dev-server-wrapper.html @@ -0,0 +1,82 @@ + + + + + + Dev Server + + + + + + +
+ +
+ + diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..e1ef0b9 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,6 @@ +export default { + globalSetup: './test/global-setup.js', + globalTeardown: './test/global-teardown.js', + testEnvironment: './test/puppeteer-environment.js', + reporters: ['default', '/test/utils/image-reporter.js'], +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..ccfe29d --- /dev/null +++ b/package.json @@ -0,0 +1,85 @@ +{ + "name": "google-plugin", + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "lint": "eslint .", + "login": "clasp login", + "setup": "rimraf .clasp.json && mkdirp dist && clasp create --type sheets --title \"My React Project\" --rootDir ./dist && mv ./dist/.clasp.json ./.clasp.json && rimraf dist", + "open": "clasp open --addon", + "push": "clasp push", + "setup:https": "mkdirp certs && mkcert -key-file ./certs/key.pem -cert-file ./certs/cert.pem localhost 127.0.0.1", + "build:dev": "tsc && vite build --mode development", + "build": "tsc && vite build --mode production", + "deploy:dev": "yarn build:dev && yarn push", + "deploy": "yarn build && yarn push", + "start": "yarn deploy:dev && yarn dev" + }, + "keywords": [ + "react", + "google", + "apps", + "script", + "sheets" + ], + "author": "Boris Edigariani", + "license": "MIT", + "engines": { + "node": ">=10.0.0", + "npm": ">=6.0.0" + }, + "dependencies": { + "@emotion/react": "^11.10.6", + "@emotion/styled": "^11.10.6", + "@mui/material": "^5.11.11", + "gas-client": "^1.1.1", + "nanoid": "^5.0.7", + "prop-types": "^15.8.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-transition-group": "^4.4.2" + }, + "devDependencies": { + "@babel/preset-env": "^7.24.6", + "@google/clasp": "^2.4.2", + "@types/expect-puppeteer": "^5.0.0", + "@types/jest-environment-puppeteer": "^5.0.2", + "@types/node": "^20.14.9", + "@types/puppeteer": "^5.4.6", + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "@typescript-eslint/eslint-plugin": "^7.2.0", + "@typescript-eslint/parser": "^7.2.0", + "@vitejs/plugin-react-swc": "^3.5.0", + "autoprefixer": "^10.4.19", + "aws-sdk": "^2.1106.0", + "cross-env": "^7.0.3", + "dotenv": "^16.4.5", + "eslint": "^8.57.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-googleappsscript": "^1.0.4", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jest": "^26.5.3", + "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.6", + "gas-types-detailed": "^1.1.2", + "jest": "^28.1.1", + "jest-environment-node": "^28.1.1", + "jest-image-snapshot": "^5.1.0", + "postcss": "^8.4.38", + "postcss-preset-env": "^9.5.4", + "prettier": "^2.7.0", + "puppeteer": "^14.3.0", + "puppeteer-extra": "^3.2.3", + "puppeteer-extra-plugin-stealth": "^2.9.0", + "rollup": "^4.18.0", + "tailwindcss": "^3.4.3", + "typescript": "^5.2.2", + "vite": "^5.2.0", + "vite-plugin-singlefile": "^2.0.1", + "vite-plugin-static-copy": "^1.0.1" + } +} diff --git a/postcss.config.cjs b/postcss.config.cjs new file mode 100644 index 0000000..12a703d --- /dev/null +++ b/postcss.config.cjs @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/scripts/generate-cert.ps1 b/scripts/generate-cert.ps1 new file mode 100644 index 0000000..bf6c3cb --- /dev/null +++ b/scripts/generate-cert.ps1 @@ -0,0 +1,40 @@ +# reference code: https://stackoverflow.com/questions/70226493/webpack-dev-server-and-https-this-site-can-t-be-reached +# reference issue: https://github.com/FiloSottile/mkcert/issues/286 + +$dnsName = "localhost" +$expiry = [DateTime]::Now.AddYears(1); +$repoRoot = Split-Path $PSScriptRoot +$certsDir = "$repoRoot\certs"; +$fileName = "cert.pfx"; +$passwordText = "abc123"; +$name = "ReactApp"; + +Write-Host "Creating cert directly into CurrentUser\My store" + +$certificate = New-SelfSignedCertificate ` + -KeyExportPolicy 'Exportable' ` + -CertStoreLocation Cert:\CurrentUser\My ` + -Subject $name ` + -FriendlyName $name ` + -DnsName $dnsName ` + -NotAfter $expiry + +$certFile = Join-Path $certsDir $fileName + +Write-Host "Exporting certificate to $certFile" + +$password = ConvertTo-SecureString ` + -String $passwordText ` + -Force -AsPlainText + +Export-PfxCertificate ` + -Cert $certificate ` + -FilePath $certFile ` + -Password $password | Out-Null + +Write-Host "Importing $certFile to CurrentUser\Root store for immediate system wide trust" + +Import-PfxCertificate ` + -FilePath $certFile ` + -CertStoreLocation Cert:\LocalMachine\Root ` + -Password $password | Out-Null \ No newline at end of file diff --git a/src/client/.eslintrc.json b/src/client/.eslintrc.json new file mode 100644 index 0000000..f6719c8 --- /dev/null +++ b/src/client/.eslintrc.json @@ -0,0 +1,45 @@ +{ + "root": true, + "extends": [ + "airbnb-base", + "plugin:prettier/recommended", + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended" + ], + "plugins": ["react-refresh", "prettier"], + "rules": { + "prettier/prettier": "error", + "camelcase": "warn", + "import/prefer-default-export": "warn", + "import/no-extraneous-dependencies": "warn", + "prefer-object-spread": "warn", + "spaced-comment": "off", + "react-refresh/only-export-components": [ + "warn", + { "allowConstantExport": true } + ], + "import/extensions": [ + "error", + "ignorePackages", + { + "js": "never", + "jsx": "never", + "ts": "never", + "tsx": "never" + } + ] + }, + "settings": { + "react": { + "version": "detect" + }, + "import/resolver": { + "node": { + "extensions": [".js", ".jsx", ".ts", ".tsx"] + } + } + }, + "env": { "browser": true, "es2020": true }, + "parser": "@typescript-eslint/parser" +} diff --git a/src/client/README.md b/src/client/README.md new file mode 100644 index 0000000..52ba76f --- /dev/null +++ b/src/client/README.md @@ -0,0 +1,31 @@ +# Client (our React code) + +This directory is where we store the source code for our client-side React apps. + +We have multiple directories in here because our app creates menu items that open multiple dialog windows. Each dialog opens a separate app, so each directory here represents its own distinct React app. Our webpack configuration will generate a separate bundle for each React app. + +## Requirements + +Each React app will need: +- an entrypoint, usually a file named `index.js`, that loads the app +- an HTML file that acts as a template, in which the bundled React app is loaded + +You'll need to declare the following in [webpack.config.js](../../webpack.config.js): +- **name**: just a name to print in the webpack console, e.g. 'CLIENT - Dialog Demo' +- **entry**: the path to the entry point for the app, e.g. './src/client/dialog-demo/index.js' +- **filename**: the name of the html file that is generated. The server code will reference this filename to load the app into a dialog window. E.g. 'dialog-demo' +- **template**: the path to the HTML template for the app, e.g. './src/client/dialog-demo/index.html' + + +### Adding or removing an entrypoint +Your app or use case may only require a single dialog or sidebar, or you may want to add more than are included in the sample app. + +To edit the entrypoints, you will need to: + +1. Create or remove the entrypoint directories in the client source code. For instance, you can remove `./src/client/sidebar-about-page` altogether, or copy it and modify the source code. See above [requirements](#requirements). + +2. Modify the server-side code to load the correct menu items and expose the correct public functions: + - [ui file](../server/ui.js) + - [index file](../server/index.js) + +3. Modify the `clientEntrypoints` config in the [webpack config file](../../webpack.config.js). diff --git a/src/client/dialog/components/Dialog.tsx b/src/client/dialog/components/Dialog.tsx new file mode 100644 index 0000000..0c12a64 --- /dev/null +++ b/src/client/dialog/components/Dialog.tsx @@ -0,0 +1,12 @@ +const Dialog = () => { + return ( +
+