grist-widget-sdk 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +730 -0
- package/LICENSE +15 -0
- package/README.md +249 -0
- package/dist/advanced.cjs +2749 -0
- package/dist/advanced.cjs.map +1 -0
- package/dist/advanced.d.cts +790 -0
- package/dist/advanced.d.ts +790 -0
- package/dist/advanced.js +825 -0
- package/dist/advanced.js.map +1 -0
- package/dist/chunk-64SVRWW7.js +758 -0
- package/dist/chunk-64SVRWW7.js.map +1 -0
- package/dist/chunk-BNBV24WM.js +479 -0
- package/dist/chunk-BNBV24WM.js.map +1 -0
- package/dist/chunk-ELGVOHFD.js +32 -0
- package/dist/chunk-ELGVOHFD.js.map +1 -0
- package/dist/chunk-GTTD5HPO.js +36 -0
- package/dist/chunk-GTTD5HPO.js.map +1 -0
- package/dist/chunk-JASMM2YC.js +243 -0
- package/dist/chunk-JASMM2YC.js.map +1 -0
- package/dist/chunk-PM3B4WYV.js +12 -0
- package/dist/chunk-PM3B4WYV.js.map +1 -0
- package/dist/chunk-QKXKZ27D.js +1189 -0
- package/dist/chunk-QKXKZ27D.js.map +1 -0
- package/dist/chunk-QZG7SNUG.js +463 -0
- package/dist/chunk-QZG7SNUG.js.map +1 -0
- package/dist/chunk-REV5AUXD.js +25 -0
- package/dist/chunk-REV5AUXD.js.map +1 -0
- package/dist/chunk-XE2SKNLH.js +1641 -0
- package/dist/chunk-XE2SKNLH.js.map +1 -0
- package/dist/create-grist-emulator-C_0I4wax.d.ts +80 -0
- package/dist/create-grist-emulator-DBS6gmiI.d.cts +80 -0
- package/dist/emulator/devtools.cjs +1367 -0
- package/dist/emulator/devtools.cjs.map +1 -0
- package/dist/emulator/devtools.d.cts +46 -0
- package/dist/emulator/devtools.d.ts +46 -0
- package/dist/emulator/devtools.js +480 -0
- package/dist/emulator/devtools.js.map +1 -0
- package/dist/emulator/testing.cjs +2061 -0
- package/dist/emulator/testing.cjs.map +1 -0
- package/dist/emulator/testing.d.cts +50 -0
- package/dist/emulator/testing.d.ts +50 -0
- package/dist/emulator/testing.js +124 -0
- package/dist/emulator/testing.js.map +1 -0
- package/dist/emulator/transports.cjs +1132 -0
- package/dist/emulator/transports.cjs.map +1 -0
- package/dist/emulator/transports.d.cts +37 -0
- package/dist/emulator/transports.d.ts +37 -0
- package/dist/emulator/transports.js +12 -0
- package/dist/emulator/transports.js.map +1 -0
- package/dist/emulator.cjs +1796 -0
- package/dist/emulator.cjs.map +1 -0
- package/dist/emulator.d.cts +107 -0
- package/dist/emulator.d.ts +107 -0
- package/dist/emulator.js +26 -0
- package/dist/emulator.js.map +1 -0
- package/dist/index.cjs +5511 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +825 -0
- package/dist/index.d.ts +825 -0
- package/dist/index.js +2447 -0
- package/dist/index.js.map +1 -0
- package/dist/inline-CRwQQ-GW.d.cts +311 -0
- package/dist/inline-QitZwXKP.d.ts +311 -0
- package/dist/templates-BC7FoSdW.d.ts +24 -0
- package/dist/templates-DBq719O_.d.cts +24 -0
- package/dist/types-Fb2wplkv.d.cts +228 -0
- package/dist/types-Fb2wplkv.d.ts +228 -0
- package/dist/use-grist-widget-options-from-context-BFhQBGRY.d.ts +522 -0
- package/dist/use-grist-widget-options-from-context-qpFMdoBJ.d.cts +522 -0
- package/package.json +131 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Arthur Blanchon
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# grist-widget-sdk
|
|
2
|
+
|
|
3
|
+
React hooks and TypeScript utilities for building Grist widgets.
|
|
4
|
+
|
|
5
|
+
## Why not use `grist` directly?
|
|
6
|
+
|
|
7
|
+
Grist’s [`grist-plugin-api.js`](https://docs.getgrist.com/grist-plugin-api.js) exposes a global `grist` object. For React widgets, **`grist-widget-sdk`** replaces hand-rolled glue:
|
|
8
|
+
|
|
9
|
+
| Raw `grist` | SDK |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| `grist.ready` + `onRecord` callbacks | `<GristWidgetProvider>` + `useGrist()` with `mode` |
|
|
12
|
+
| Wire-format dates/refs | Decoded values by default |
|
|
13
|
+
| `mapColumnNames` / tuples for writes | `mappedRecord`, `mapBack`, `table.update` |
|
|
14
|
+
| No test harness | `renderWithGrist` + emulator presets |
|
|
15
|
+
|
|
16
|
+
Full comparison: [Raw plugin API vs SDK](https://github.com/ArthurBlanchon/grist-widget-sdk/blob/main/apps/docs/guide/raw-plugin-api-vs-sdk.md) (also in the published docs site under **Guide → Raw API vs SDK**).
|
|
17
|
+
|
|
18
|
+
## What This Package Provides
|
|
19
|
+
|
|
20
|
+
- Main API via `grist-widget-sdk` (hooks, types, helpers)
|
|
21
|
+
- `grist-widget-sdk/advanced` — lower-level hooks (`useGristReady`, `useGristHandshake`, …)
|
|
22
|
+
- `grist-widget-sdk/emulator` subpath for emulator utilities
|
|
23
|
+
- ESM + CJS + type declaration builds (`.d.ts`)
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pnpm add grist-widget-sdk
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Required peer dependencies:
|
|
32
|
+
|
|
33
|
+
- `react >= 18`
|
|
34
|
+
- `react-dom >= 18`
|
|
35
|
+
|
|
36
|
+
## Grist Runtime Script (Required)
|
|
37
|
+
|
|
38
|
+
This package expects a global `grist` object at runtime.
|
|
39
|
+
|
|
40
|
+
Load the official Grist plugin API script in your app shell/layout:
|
|
41
|
+
|
|
42
|
+
```html
|
|
43
|
+
<script src="https://docs.getgrist.com/grist-plugin-api.js"></script>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick Start Next.js (`app/layout.tsx`)
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import Script from "next/script"
|
|
50
|
+
import { GristBoundary, GristWidgetProvider } from "grist-widget-sdk"
|
|
51
|
+
|
|
52
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
53
|
+
return (
|
|
54
|
+
<html lang="en">
|
|
55
|
+
<body>
|
|
56
|
+
<Script src="https://docs.getgrist.com/grist-plugin-api.js" />
|
|
57
|
+
<GristWidgetProvider options={{ requiredAccess: "read table" }}>
|
|
58
|
+
<GristBoundary>
|
|
59
|
+
{children}
|
|
60
|
+
</GristBoundary>
|
|
61
|
+
</GristWidgetProvider>
|
|
62
|
+
</body>
|
|
63
|
+
</html>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Then render widget content from your page, for example `app/page.tsx`:
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { useGrist } from "grist-widget-sdk"
|
|
72
|
+
|
|
73
|
+
export default function Page() {
|
|
74
|
+
const w = useGrist()
|
|
75
|
+
|
|
76
|
+
if (w.status === "booting") return <p>Loading...</p>
|
|
77
|
+
if (w.mode === "empty") return <p>Select a row.</p>
|
|
78
|
+
if (w.mode === "new-row") return <p>New row flow</p>
|
|
79
|
+
|
|
80
|
+
return <p>Record id: {String(w.record?.id ?? "")}</p>
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Quick Start Vite (`src/main.tsx`)
|
|
85
|
+
|
|
86
|
+
In `index.html`, load the script:
|
|
87
|
+
|
|
88
|
+
```html
|
|
89
|
+
<script src="https://docs.getgrist.com/grist-plugin-api.js"></script>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Then mount your widget root:
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
import { StrictMode } from "react"
|
|
96
|
+
import { createRoot } from "react-dom/client"
|
|
97
|
+
import { GristBoundary, GristWidgetProvider, useGrist } from "grist-widget-sdk"
|
|
98
|
+
|
|
99
|
+
function WidgetContent() {
|
|
100
|
+
const w = useGrist()
|
|
101
|
+
|
|
102
|
+
if (w.status === "booting") return <p>Loading...</p>
|
|
103
|
+
if (w.mode === "empty") return <p>Select a row.</p>
|
|
104
|
+
if (w.mode === "new-row") return <p>New row flow</p>
|
|
105
|
+
|
|
106
|
+
return <p>Record id: {String(w.record?.id ?? "")}</p>
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function App() {
|
|
110
|
+
return (
|
|
111
|
+
<GristWidgetProvider options={{ requiredAccess: "read table" }}>
|
|
112
|
+
<GristBoundary>
|
|
113
|
+
<WidgetContent />
|
|
114
|
+
</GristBoundary>
|
|
115
|
+
</GristWidgetProvider>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
createRoot(document.getElementById("root")!).render(
|
|
120
|
+
<StrictMode>
|
|
121
|
+
<App />
|
|
122
|
+
</StrictMode>
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Exports
|
|
127
|
+
|
|
128
|
+
Main entrypoint:
|
|
129
|
+
|
|
130
|
+
- `grist-widget-sdk` — production surface:
|
|
131
|
+
- Components: `GristWidgetProvider`, `GristBoundary`, `useGristContext`, `useGristOptional`
|
|
132
|
+
- Primary hook: `useGrist` (includes `capabilities` — `canRender`, `canWriteRecords`, …)
|
|
133
|
+
- Slice hooks: `useGristStatus`, `useGristSelection`, `useGristWrites`, `useGristTheme`
|
|
134
|
+
- Schema hook: `useGristSchema`
|
|
135
|
+
- Helpers: action builders, value codec, safe parsing, replica builders,
|
|
136
|
+
REST auth, attachments, column-mapping validation, alerts.
|
|
137
|
+
|
|
138
|
+
Subpath exports:
|
|
139
|
+
|
|
140
|
+
- `grist-widget-sdk/advanced` — low-level / mid-level hooks for power users
|
|
141
|
+
(`useGristReady`, `useGristAvailability`, `useGristRecord`, `useGristRecords`,
|
|
142
|
+
`useGristActions`, `useGristTableOps`, `useGristMappedRecord`,
|
|
143
|
+
`useGristRowsFromTable`, `useGristAccessToken`, `useGristSection`,
|
|
144
|
+
`useGristWidgetOptions`, `useGristTable`).
|
|
145
|
+
- `grist-widget-sdk/emulator` — `createGristEmulator`, `parseDocument`,
|
|
146
|
+
`safeParseDocument`, `presets`, emulator types.
|
|
147
|
+
|
|
148
|
+
## Replica document (`GristReplicaDocument`)
|
|
149
|
+
|
|
150
|
+
One JSON shape is used for **`useGristSchema()`**, **`getDocumentJson` / `buildDocumentJson`**, playground **`*.grist.json`** fixtures, and **`createGristEmulator({ document })`**. Valid fixtures are checked with **`parseDocument`** (Zod).
|
|
151
|
+
|
|
152
|
+
### Top-level fields
|
|
153
|
+
|
|
154
|
+
| Field | Required | Notes |
|
|
155
|
+
| --- | --- | --- |
|
|
156
|
+
| `generatedAt` | yes | ISO 8601 string |
|
|
157
|
+
| `tables` | yes | `Record<tableId, GristReplicaTable>` — keys align with `docApi.listTables()` / `fetchTable(tableId)` |
|
|
158
|
+
| `source` | no | Provenance (e.g. pass `useGristSchema({ source })` or `buildReplicaDocumentFromDocApi(docApi, { source })`); hosts may log it |
|
|
159
|
+
| `docName`, `mode`, `selection`, `widgetOptions`, `widgetInteraction`, `configuration`, `accessToken` | no | Same semantics as in types |
|
|
160
|
+
|
|
161
|
+
Optional **`mode`**: `"schema-only"` \| `"schema+samples"` \| `"schema+data"` (hint only). At boundaries, use **`normalizeReplicaTableInput`** / **`normalizeReplicaDocumentInput`** so missing **`rows`** behaves like **`[]`**.
|
|
162
|
+
|
|
163
|
+
### Tables and rows
|
|
164
|
+
|
|
165
|
+
- **`GristReplicaTable`**: optional `label`, `columns`, `rows`, `rowCount`, `dataOmitted`, etc.
|
|
166
|
+
- Row bodies are **`GristReplicaRow`** (`{ id: number, … }`), **not** columnar `fetchTable` wire format. Use **`buildReplicaDocumentFromDocApi`** or **`decodeColumnarToReplicaRows`** to build rows from `docApi.fetchTable`.
|
|
167
|
+
|
|
168
|
+
### Building and serializing
|
|
169
|
+
|
|
170
|
+
- Prefer **`buildReplicaDocumentFromDocApi(docApi, { includeRows: false \| "samples" \| "full", sampleRowLimit, source, includeSystemTableData })`**, or **`w.buildReplicaDocumentFromDocApi(...)`** from **`useGrist()`**.
|
|
171
|
+
- **`getDocumentJson` / `buildDocumentJson`** accept **`includeRows`**: `"none"` \| `"samples"` \| `"full"` (plus optional **`sampleRowLimit`**, **`space`**).
|
|
172
|
+
|
|
173
|
+
### Emulator
|
|
174
|
+
|
|
175
|
+
Only a full **`GristReplicaDocument`** is accepted: **`createGristEmulator({ document })`** and **`parseDocument`**. There is no legacy coercion or alternate “emulator input” type; assemble the object (or load JSON), then **`parseDocument`** if you need validation.
|
|
176
|
+
|
|
177
|
+
The canonical design document for this shape lives at **`apps/docs/design/replica-document.md`**.
|
|
178
|
+
- `grist-widget-sdk/emulator/transports` — low-level `iframe` and `inline` transports if you need to wire your own host.
|
|
179
|
+
- `grist-widget-sdk/emulator/devtools` — `<EmulatorInspector />` React component (table editor + event timeline + options).
|
|
180
|
+
- `grist-widget-sdk/emulator/testing` — `renderWithGrist(...)`, vitest
|
|
181
|
+
matchers (`actionsOf`, `eventsOf`, `waitForEvent`), document `presets`,
|
|
182
|
+
and the most-used `@testing-library/react` primitives (`screen`,
|
|
183
|
+
`fireEvent`, `waitFor`, `act`, `cleanup`, `within`, `render`) so a typical
|
|
184
|
+
test file only needs one import line.
|
|
185
|
+
|
|
186
|
+
## Emulator (SOTA)
|
|
187
|
+
|
|
188
|
+
The emulator is the single source of truth for three use-cases:
|
|
189
|
+
|
|
190
|
+
1. **Demoing widgets** — production-fidelity by loading the *real*
|
|
191
|
+
`grist-plugin-api.js` inside an iframe and speaking `grain-rpc` over
|
|
192
|
+
`postMessage`:
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
import { createGristEmulator, presets } from "grist-widget-sdk/emulator"
|
|
196
|
+
import { EmulatorInspector } from "grist-widget-sdk/emulator/devtools"
|
|
197
|
+
|
|
198
|
+
const iframe = document.querySelector("iframe")!
|
|
199
|
+
const emulator = createGristEmulator({
|
|
200
|
+
document: presets.todoList(),
|
|
201
|
+
transport: { kind: "iframe", iframe },
|
|
202
|
+
})
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
2. **Robust SDK testing** — `renderWithGrist` synthesises `window.grist`
|
|
206
|
+
in-process, so every `useGrist` test goes through the *exact* same code
|
|
207
|
+
path without ceremony:
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
import { renderWithGrist } from "grist-widget-sdk/emulator/testing"
|
|
211
|
+
|
|
212
|
+
const { emulator, getByText } = renderWithGrist(<MyWidget />, {
|
|
213
|
+
emulator: { document: presets.contacts() },
|
|
214
|
+
})
|
|
215
|
+
emulator.mutate.addRow("Contacts", { firstName: "Grace", lastName: "Hopper" })
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
3. **Live IDE preview** — same `inline` transport, mounted next to the
|
|
219
|
+
widget to give a Storybook-grade dev loop.
|
|
220
|
+
|
|
221
|
+
## Local Development
|
|
222
|
+
|
|
223
|
+
From the workspace root:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
pnpm --filter grist-widget-sdk build
|
|
227
|
+
pnpm --filter grist-widget-sdk dev
|
|
228
|
+
pnpm --filter grist-widget-sdk test
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Pre-Commit / Pre-Publish Checks
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
pnpm --filter grist-widget-sdk build
|
|
235
|
+
pnpm --filter grist-widget-sdk exec tsc --noEmit
|
|
236
|
+
pnpm --filter grist-widget-sdk exec vitest run
|
|
237
|
+
pnpm dlx @arethetypeswrong/cli -P packages/core
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
If no tests exist yet:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
pnpm --filter grist-widget-sdk exec vitest run --passWithNoTests
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Notes
|
|
247
|
+
|
|
248
|
+
- This package targets pure TypeScript/React usage (no Next.js dependency required).
|
|
249
|
+
- ATTW may report a `node10` warning for the `grist-widget-sdk/emulator` subpath. This is expected for modern `exports`-based packages and is non-blocking for Node 16+ and modern bundlers.
|