Skip to content

Commit a1074cb

Browse files
authored
Add scriptHostFunctions for google.script.host (#36)
Now wraps google.script.host functions
1 parent b9215d8 commit a1074cb

17 files changed

+532
-249
lines changed

.eslintrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"rules": {
2424
"prettier/prettier": "error",
2525
"camelcase": "warn",
26-
"import/prefer-default-export": "warn",
26+
"import/prefer-default-export": "off",
2727
"import/no-extraneous-dependencies": "warn",
2828
"prefer-object-spread": "warn",
2929
"@typescript-eslint/indent": "off",

.github/workflows/publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212

1313
strategy:
1414
matrix:
15-
node-version: [14, 16, 18]
15+
node-version: [18, 20, 22]
1616

1717
steps:
1818
- uses: actions/checkout@v2
@@ -29,7 +29,7 @@ jobs:
2929
- uses: actions/checkout@v2
3030
- uses: actions/setup-node@v2
3131
with:
32-
node-version: 18
32+
node-version: 22
3333
registry-url: https://registry.npmjs.org/
3434
- run: yarn
3535
- run: npm publish

README.md

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,47 @@ async () => {
8484

8585
Now we can use familiar Promises in our client-side code and have easy access to all server functions.
8686

87+
### Using the scriptHostFunctions
88+
89+
The `GASClient` also provides a `scriptHostFunctions` property that gives you access to Google's script host functions, which allow you to control UI elements like dialogs and sidebars:
90+
91+
```javascript
92+
import { GASClient } from 'gas-client';
93+
const { scriptHostFunctions } = new GASClient();
94+
95+
// Close the current dialog or sidebar
96+
scriptHostFunctions.close();
97+
98+
// Set the height of the current dialog (in pixels)
99+
scriptHostFunctions.setHeight(500);
100+
101+
// Set the width of the current dialog (in pixels)
102+
scriptHostFunctions.setWidth(400);
103+
104+
// Switch focus from dialog/sidebar to the editor
105+
scriptHostFunctions.focusEditor(); // calls google.script.host.editor.focus()
106+
```
107+
108+
These functions provide the same functionality as the corresponding methods in `google.script.host` but work in both production and development environments:
109+
110+
- `close()`: Closes the current dialog or sidebar
111+
- `setHeight(height)`: Sets the height of the current dialog (in pixels)
112+
- `setWidth(width)`: Sets the width of the current dialog (in pixels)
113+
- `focusEditor()`: Switches browser focus from the dialog or sidebar to the Google Docs, Sheets, or Forms editor
114+
115+
See reference here: https://developers.google.com/apps-script/guides/html/reference/host
116+
117+
In production, these functions call the native Google Apps Script host methods directly. In development mode, they dispatch appropriate messages to the parent iframe. See section below on how to set up the parent iframe.
118+
119+
## Setting up the dev server wrapper
120+
121+
Use the [React-Google-Apps-Script](https://github.com/enuchi/React-Google-Apps-Script/) project to get started. Or reference the [dev server wrapper](https://github.com/enuchi/React-Google-Apps-Script/blob/main/dev/dev-server-wrapper.html#L40-L41) on how to set up the parent wrapper to work with `gas-client`.
122+
87123
---
88124

89125
## Typescript
90126

91-
This project now supports typescript!
92-
93-
To use it, simply import your server functions and pass them as a type parameter when creating your server.
127+
This project supports typescript. To use it, simply import your server functions and pass them as a type parameter when creating your server.
94128

95129
### On your server-side code
96130

@@ -152,19 +186,25 @@ The config object takes:
152186

153187
### Production mode
154188

155-
In the normal Google Apps Script production environment, a `new GASClient()` instance will have one available method:
189+
In the normal Google Apps Script production environment, a `new GASClient()` instance will have the following available methods:
156190

157191
- `serverFunctions`: an object containing all publicly exposed server functions (see example above).
192+
- `scriptHostFunctions`: an object containing methods to interact with the script host UI, including:
193+
- `close()`: Close the current dialog or sidebar
194+
- `setHeight(height)`: Set the dialog height in pixels
195+
- `setWidth(width)`: Set the dialog width in pixels
196+
- `focusEditor()`: Switch focus from dialog/sidebar to the editor
158197

159198
Note that `allowedDevelopmentDomains` and `parentTargetOrigin` configurations will be ignored in production, so the same code can and should be used for development and production.
160199

161200
### Development mode
162201

163202
Development mode for the `gas-client` helper class will be run when the `google` client API cannot be loaded.
164203

165-
Calling `new GASClient({ allowedDevelopmentDomains })` will create an instance with the following method in development mode:
204+
Calling `new GASClient({ allowedDevelopmentDomains })` will create an instance with the following methods in development mode:
166205

167206
- `serverFunctions`: a proxy object, used for development purposes, that mimics calling `google.script.run`. It will dispatch a message to the parent iframe (our custom Dev Server), which will call an app that actually interacts with the `google.script.run` API. Development mode will also handle the response and resolve or reject based on the response type. See the implementation for details on the event signature.
207+
- `scriptHostFunctions`: a proxy object that simulates the behavior of `google.script.host` methods in development mode by sending appropriate messages to the parent iframe.
168208

169209
## Contributors
170210

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "gas-client",
3-
"version": "1.1.1",
3+
"version": "1.2.0",
44
"description": "A client-side utility class that can call server-side Google Apps Script functions",
55
"main": "dist/index.js",
66
"typings": "dist/index.d.ts",
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { FunctionMap, ServerFunctions } from '../types/functions';
22

3-
abstract class FunctionHost<FM extends FunctionMap> {
3+
abstract class FunctionProvider<FM extends FunctionMap> {
44
protected _serverFunctions: ServerFunctions<FM> = {} as ServerFunctions<FM>;
55

66
get serverFunctions(): ServerFunctions<FM> {
77
return this._serverFunctions;
88
}
99
}
1010

11-
export { FunctionHost };
11+
export { FunctionProvider };

src/classes/gas-promises.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { ignoredFunctionNames } from '../utils/ignored-function-names';
22
import { promisify } from '../utils/promisify';
3-
import { FunctionHost } from './function-host';
3+
import { FunctionProvider } from './function-provider';
44
import { FunctionMap, ServerFunctions } from '../types/functions';
55

6-
class GASPromises<FM extends FunctionMap> extends FunctionHost<FM> {
6+
class GASPromises<FM extends FunctionMap> extends FunctionProvider<FM> {
77
constructor() {
88
super();
99
this.promisifyGASFunctions();

src/classes/host-functions.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { ScriptHostProvider } from './script-host-provider';
2+
3+
class ScriptHostFunctions extends ScriptHostProvider {
4+
constructor() {
5+
super();
6+
this.initializeScriptHostFunctions();
7+
}
8+
9+
private initializeScriptHostFunctions(): void {
10+
this._hostFunctions = {
11+
close: google.script.host.close,
12+
setHeight: google.script.host.setHeight,
13+
setWidth: google.script.host.setWidth,
14+
focusEditor: google.script.host.editor.focus,
15+
};
16+
}
17+
}
18+
19+
export { ScriptHostFunctions };
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { HostFunctions } from '../types/functions';
2+
3+
abstract class ScriptHostProvider {
4+
protected _hostFunctions = {} as HostFunctions;
5+
6+
get hostFunctions(): HostFunctions {
7+
return this._hostFunctions;
8+
}
9+
}
10+
11+
export { ScriptHostProvider };

src/classes/script-host-proxy.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { proxyHandlerScriptHostFunction } from '../utils/proxy-handler';
2+
import { HostFunctions } from '../types/functions';
3+
import { ScriptHostProvider } from './script-host-provider';
4+
5+
class ScriptHostProxy extends ScriptHostProvider {
6+
constructor() {
7+
super();
8+
this._hostFunctions = new Proxy({}, { get: proxyHandlerScriptHostFunction }) as HostFunctions;
9+
}
10+
}
11+
12+
export { ScriptHostProxy };

src/classes/server-proxy.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
import { checkAllowList } from '../utils/check-allow-list';
2-
import { proxyHandler } from '../utils/proxy-handler';
3-
import { FunctionHost } from './function-host';
4-
import { AppWindow } from '../types/dev-server';
2+
import { proxyHandlerServerFunction } from '../utils/proxy-handler';
3+
import { FunctionProvider } from './function-provider';
4+
import { AppWindow, MessageType, ResponseStatus } from '../types/dev-server';
55
import { FunctionMap, ServerFunctions } from '../types/functions';
66
import { ServerConfig } from '../types/config';
77

88
declare const window: AppWindow;
99

10-
class ServerProxy<FM extends FunctionMap> extends FunctionHost<FM> {
10+
class ServerProxy<FM extends FunctionMap> extends FunctionProvider<FM> {
1111
constructor(private _config?: ServerConfig) {
1212
super();
1313
window.gasStore = {};
1414
window.addEventListener('message', this.buildMessageListener(), false);
15-
this._serverFunctions = new Proxy({}, { get: proxyHandler }) as ServerFunctions<FM>;
15+
this._serverFunctions = new Proxy({}, { get: proxyHandlerServerFunction }) as ServerFunctions<FM>;
1616
}
1717

1818
private buildMessageListener(): (event: MessageEvent) => void {
1919
return (event: MessageEvent) => {
2020
const allowOrigin = checkAllowList(event.origin, this._config?.allowedDevelopmentDomains);
21-
if (!allowOrigin || event.data.type !== 'RESPONSE') return;
21+
if (!allowOrigin || event.data.type !== MessageType.RESPONSE) return;
2222

2323
const { response, status, id } = event.data;
2424
const { resolve, reject } = window.gasStore[id];
2525

26-
if (status === 'ERROR') {
26+
if (status === ResponseStatus.ERROR) {
2727
reject(response);
2828
}
2929
resolve(response);

0 commit comments

Comments
 (0)