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.
Files changed (70) hide show
  1. package/CHANGELOG.md +730 -0
  2. package/LICENSE +15 -0
  3. package/README.md +249 -0
  4. package/dist/advanced.cjs +2749 -0
  5. package/dist/advanced.cjs.map +1 -0
  6. package/dist/advanced.d.cts +790 -0
  7. package/dist/advanced.d.ts +790 -0
  8. package/dist/advanced.js +825 -0
  9. package/dist/advanced.js.map +1 -0
  10. package/dist/chunk-64SVRWW7.js +758 -0
  11. package/dist/chunk-64SVRWW7.js.map +1 -0
  12. package/dist/chunk-BNBV24WM.js +479 -0
  13. package/dist/chunk-BNBV24WM.js.map +1 -0
  14. package/dist/chunk-ELGVOHFD.js +32 -0
  15. package/dist/chunk-ELGVOHFD.js.map +1 -0
  16. package/dist/chunk-GTTD5HPO.js +36 -0
  17. package/dist/chunk-GTTD5HPO.js.map +1 -0
  18. package/dist/chunk-JASMM2YC.js +243 -0
  19. package/dist/chunk-JASMM2YC.js.map +1 -0
  20. package/dist/chunk-PM3B4WYV.js +12 -0
  21. package/dist/chunk-PM3B4WYV.js.map +1 -0
  22. package/dist/chunk-QKXKZ27D.js +1189 -0
  23. package/dist/chunk-QKXKZ27D.js.map +1 -0
  24. package/dist/chunk-QZG7SNUG.js +463 -0
  25. package/dist/chunk-QZG7SNUG.js.map +1 -0
  26. package/dist/chunk-REV5AUXD.js +25 -0
  27. package/dist/chunk-REV5AUXD.js.map +1 -0
  28. package/dist/chunk-XE2SKNLH.js +1641 -0
  29. package/dist/chunk-XE2SKNLH.js.map +1 -0
  30. package/dist/create-grist-emulator-C_0I4wax.d.ts +80 -0
  31. package/dist/create-grist-emulator-DBS6gmiI.d.cts +80 -0
  32. package/dist/emulator/devtools.cjs +1367 -0
  33. package/dist/emulator/devtools.cjs.map +1 -0
  34. package/dist/emulator/devtools.d.cts +46 -0
  35. package/dist/emulator/devtools.d.ts +46 -0
  36. package/dist/emulator/devtools.js +480 -0
  37. package/dist/emulator/devtools.js.map +1 -0
  38. package/dist/emulator/testing.cjs +2061 -0
  39. package/dist/emulator/testing.cjs.map +1 -0
  40. package/dist/emulator/testing.d.cts +50 -0
  41. package/dist/emulator/testing.d.ts +50 -0
  42. package/dist/emulator/testing.js +124 -0
  43. package/dist/emulator/testing.js.map +1 -0
  44. package/dist/emulator/transports.cjs +1132 -0
  45. package/dist/emulator/transports.cjs.map +1 -0
  46. package/dist/emulator/transports.d.cts +37 -0
  47. package/dist/emulator/transports.d.ts +37 -0
  48. package/dist/emulator/transports.js +12 -0
  49. package/dist/emulator/transports.js.map +1 -0
  50. package/dist/emulator.cjs +1796 -0
  51. package/dist/emulator.cjs.map +1 -0
  52. package/dist/emulator.d.cts +107 -0
  53. package/dist/emulator.d.ts +107 -0
  54. package/dist/emulator.js +26 -0
  55. package/dist/emulator.js.map +1 -0
  56. package/dist/index.cjs +5511 -0
  57. package/dist/index.cjs.map +1 -0
  58. package/dist/index.d.cts +825 -0
  59. package/dist/index.d.ts +825 -0
  60. package/dist/index.js +2447 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/inline-CRwQQ-GW.d.cts +311 -0
  63. package/dist/inline-QitZwXKP.d.ts +311 -0
  64. package/dist/templates-BC7FoSdW.d.ts +24 -0
  65. package/dist/templates-DBq719O_.d.cts +24 -0
  66. package/dist/types-Fb2wplkv.d.cts +228 -0
  67. package/dist/types-Fb2wplkv.d.ts +228 -0
  68. package/dist/use-grist-widget-options-from-context-BFhQBGRY.d.ts +522 -0
  69. package/dist/use-grist-widget-options-from-context-qpFMdoBJ.d.cts +522 -0
  70. package/package.json +131 -0
@@ -0,0 +1,124 @@
1
+ import {
2
+ resetEmbeddedHandshakeManagerForTests,
3
+ resetGristEventsForTests,
4
+ resetGristReadyCacheForTests
5
+ } from "../chunk-XE2SKNLH.js";
6
+ import {
7
+ createGristEmulator,
8
+ presets
9
+ } from "../chunk-QZG7SNUG.js";
10
+ import "../chunk-QKXKZ27D.js";
11
+ import "../chunk-GTTD5HPO.js";
12
+ import "../chunk-ELGVOHFD.js";
13
+ import "../chunk-PM3B4WYV.js";
14
+ import "../chunk-64SVRWW7.js";
15
+
16
+ // src/emulator/testing/render-with-grist.tsx
17
+ import { render } from "@testing-library/react";
18
+ function renderWithGrist(ui, options = {}) {
19
+ fakeIframeContext();
20
+ resetGristEventsForTests();
21
+ resetGristReadyCacheForTests();
22
+ resetEmbeddedHandshakeManagerForTests();
23
+ const emulator = options.existing ?? createGristEmulator(options.emulator ?? { document: presets.blank() });
24
+ const result = render(ui, options);
25
+ const originalUnmount = result.unmount;
26
+ result.unmount = () => {
27
+ originalUnmount();
28
+ if (!options.existing) emulator.dispose();
29
+ resetGristReadyCacheForTests();
30
+ resetGristEventsForTests();
31
+ resetEmbeddedHandshakeManagerForTests();
32
+ };
33
+ return Object.assign(result, { emulator });
34
+ }
35
+ function fakeIframeContext() {
36
+ if (typeof window === "undefined") return;
37
+ if (window.self !== window.top) return;
38
+ try {
39
+ Object.defineProperty(window, "top", {
40
+ configurable: true,
41
+ get: () => ({})
42
+ });
43
+ } catch {
44
+ }
45
+ }
46
+
47
+ // src/emulator/testing/matchers.ts
48
+ function actionsOf(emulator) {
49
+ return emulator.bus.history().filter((e) => e.type === "user-action").map((e) => e.action);
50
+ }
51
+ function eventsOf(emulator) {
52
+ return emulator.bus.history();
53
+ }
54
+ function predicateForKind(kind) {
55
+ switch (kind) {
56
+ case "ready":
57
+ return (e) => e.type === "ready";
58
+ case "record":
59
+ case "cursor":
60
+ return (e) => e.type === "cursor-change";
61
+ case "records":
62
+ return (e) => e.type === "data-change";
63
+ case "options":
64
+ return (e) => e.type === "options-change";
65
+ case "theme":
66
+ return (e) => e.type === "theme-change";
67
+ }
68
+ }
69
+ function waitForNextEvent(emulator, predicate, timeoutMs, label) {
70
+ return new Promise((resolve, reject) => {
71
+ const timer = setTimeout(() => {
72
+ off();
73
+ const hint = label ? ` (kind: ${label})` : "";
74
+ const recent = emulator.bus.history().slice(-5).map((e) => e.type).join(", ");
75
+ reject(
76
+ new Error(
77
+ `waitForEvent timed out after ${timeoutMs}ms${hint}.` + (recent ? ` Recent bus events: ${recent}.` : " No bus events yet.")
78
+ )
79
+ );
80
+ }, timeoutMs);
81
+ const off = emulator.bus.on((event) => {
82
+ if (predicate(event)) {
83
+ clearTimeout(timer);
84
+ off();
85
+ resolve(event);
86
+ }
87
+ });
88
+ });
89
+ }
90
+ function waitForEvent(emulator, kindOrPredicate, timeoutOrOptions = 1e3) {
91
+ if (typeof kindOrPredicate === "string") {
92
+ const opts = typeof timeoutOrOptions === "object" ? timeoutOrOptions : {};
93
+ const timeoutMs2 = opts.timeoutMs ?? 1e3;
94
+ return waitForNextEvent(emulator, predicateForKind(kindOrPredicate), timeoutMs2, kindOrPredicate);
95
+ }
96
+ const timeoutMs = typeof timeoutOrOptions === "number" ? timeoutOrOptions : 1e3;
97
+ return waitForNextEvent(emulator, kindOrPredicate, timeoutMs);
98
+ }
99
+
100
+ // src/emulator/testing/index.ts
101
+ import {
102
+ act,
103
+ cleanup,
104
+ fireEvent,
105
+ render as render2,
106
+ screen,
107
+ waitFor,
108
+ within
109
+ } from "@testing-library/react";
110
+ export {
111
+ act,
112
+ actionsOf,
113
+ cleanup,
114
+ eventsOf,
115
+ fireEvent,
116
+ presets,
117
+ render2 as render,
118
+ renderWithGrist,
119
+ screen,
120
+ waitFor,
121
+ waitForEvent,
122
+ within
123
+ };
124
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/emulator/testing/render-with-grist.tsx","../../src/emulator/testing/matchers.ts","../../src/emulator/testing/index.ts"],"sourcesContent":["/**\n * `renderWithGrist(ui, options)` — drop-in replacement for\n * `@testing-library/react`'s `render(ui)` that boots a Grist emulator\n * (inline transport) and exposes it to the test.\n *\n * The emulator is mounted as `window.grist`. The SDK hooks (`useGrist`,\n * `useGristReady`, etc.) detect it via the same path they use in production.\n *\n * Cleanup is automatic when the test's container unmounts.\n */\n\nimport type { ReactElement } from \"react\"\n\nimport { render, type RenderOptions, type RenderResult } from \"@testing-library/react\"\n\nimport { resetEmbeddedHandshakeManagerForTests } from \"../../sdk/internal/embedded-handshake-manager\"\nimport { resetGristReadyCacheForTests } from \"../../sdk/internal/ready-singleton\"\nimport { resetGristEventsForTests } from \"../../sdk/internal/subscriptions\"\nimport { createGristEmulator } from \"../create-grist-emulator\"\nimport type { CreateGristEmulatorOptions, GristEmulator } from \"../create-grist-emulator\"\nimport { presets } from \"../templates\"\n\nexport type RenderWithGristOptions = RenderOptions & {\n emulator?: CreateGristEmulatorOptions\n /** Skip mounting the emulator; you bring your own. Useful for shared fixtures. */\n existing?: GristEmulator\n}\n\nexport type RenderWithGristResult = RenderResult & {\n emulator: GristEmulator\n}\n\nexport function renderWithGrist(\n ui: ReactElement,\n options: RenderWithGristOptions = {}\n): RenderWithGristResult {\n // Make `window.self !== window.top` evaluate to `true` so\n // `useGristAvailability` reports the widget as embedded. We only do this in\n // jsdom; real browsers already enforce this distinction for iframes.\n fakeIframeContext()\n\n resetGristEventsForTests()\n resetGristReadyCacheForTests()\n resetEmbeddedHandshakeManagerForTests()\n\n const emulator =\n options.existing ??\n createGristEmulator(options.emulator ?? { document: presets.blank() })\n\n const result = render(ui, options)\n const originalUnmount = result.unmount\n result.unmount = () => {\n originalUnmount()\n if (!options.existing) emulator.dispose()\n resetGristReadyCacheForTests()\n resetGristEventsForTests()\n resetEmbeddedHandshakeManagerForTests()\n }\n return Object.assign(result, { emulator })\n}\n\nfunction fakeIframeContext() {\n if (typeof window === \"undefined\") return\n if (window.self !== window.top) return // already iframe-like\n try {\n Object.defineProperty(window, \"top\", {\n configurable: true,\n get: () => ({}),\n })\n } catch {\n // Some jsdom versions block redefinition; fail silently — tests using\n // `useGristAvailability` will need to bypass it explicitly in that case.\n }\n}\n","/**\n * Vitest helpers to assert what the widget did to the document.\n *\n * Usage:\n *\n * const { emulator } = renderWithGrist(<MyWidget />)\n * await userEvent.click(screen.getByText(\"Add\"))\n * expect(actionsOf(emulator)).toContainEqual([\"AddRecord\", \"Tasks\", null, { title: \"X\" }])\n */\n\nimport type { GristEmulator } from \"../create-grist-emulator\"\nimport type { EmulatorEvent } from \"../core/event-bus\"\n\nexport function actionsOf(emulator: GristEmulator): unknown[][] {\n return emulator.bus\n .history()\n .filter((e: EmulatorEvent): e is Extract<EmulatorEvent, { type: \"user-action\" }> => e.type === \"user-action\")\n .map((e) => e.action as unknown[])\n}\n\nexport function eventsOf(emulator: GristEmulator): readonly EmulatorEvent[] {\n return emulator.bus.history()\n}\n\n/** Bus event kinds aligned with grist host callbacks (see inline transport). */\nexport type WaitForEventKind = \"ready\" | \"record\" | \"records\" | \"options\" | \"theme\" | \"cursor\"\n\nexport type WaitForEventOptions = {\n timeoutMs?: number\n}\n\nfunction predicateForKind(kind: WaitForEventKind): (event: EmulatorEvent) => boolean {\n switch (kind) {\n case \"ready\":\n return (e) => e.type === \"ready\"\n case \"record\":\n case \"cursor\":\n return (e) => e.type === \"cursor-change\"\n case \"records\":\n return (e) => e.type === \"data-change\"\n case \"options\":\n return (e) => e.type === \"options-change\"\n case \"theme\":\n return (e) => e.type === \"theme-change\"\n }\n}\n\nfunction waitForNextEvent(\n emulator: GristEmulator,\n predicate: (event: EmulatorEvent) => boolean,\n timeoutMs: number,\n label?: string\n): Promise<EmulatorEvent> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n off()\n const hint = label ? ` (kind: ${label})` : \"\"\n const recent = emulator.bus\n .history()\n .slice(-5)\n .map((e) => e.type)\n .join(\", \")\n reject(\n new Error(\n `waitForEvent timed out after ${timeoutMs}ms${hint}.` +\n (recent ? ` Recent bus events: ${recent}.` : \" No bus events yet.\")\n )\n )\n }, timeoutMs)\n const off = emulator.bus.on((event) => {\n if (predicate(event)) {\n clearTimeout(timer)\n off()\n resolve(event)\n }\n })\n })\n}\n\nexport function waitForEvent(\n emulator: GristEmulator,\n kind: WaitForEventKind,\n options?: WaitForEventOptions\n): Promise<EmulatorEvent>\nexport function waitForEvent(\n emulator: GristEmulator,\n predicate: (event: EmulatorEvent) => boolean,\n timeoutMs?: number\n): Promise<EmulatorEvent>\nexport function waitForEvent(\n emulator: GristEmulator,\n kindOrPredicate: WaitForEventKind | ((event: EmulatorEvent) => boolean),\n timeoutOrOptions: number | WaitForEventOptions = 1000\n): Promise<EmulatorEvent> {\n if (typeof kindOrPredicate === \"string\") {\n const opts = typeof timeoutOrOptions === \"object\" ? timeoutOrOptions : {}\n const timeoutMs = opts.timeoutMs ?? 1000\n return waitForNextEvent(emulator, predicateForKind(kindOrPredicate), timeoutMs, kindOrPredicate)\n }\n const timeoutMs = typeof timeoutOrOptions === \"number\" ? timeoutOrOptions : 1000\n return waitForNextEvent(emulator, kindOrPredicate, timeoutMs)\n}\n","/**\n * Test entry point — `grist-widget-sdk/emulator/testing`.\n *\n * Bundles `renderWithGrist`, matchers, document presets, and the most-used\n * @testing-library/react primitives so a typical test file only needs one\n * import line:\n *\n * import {\n * renderWithGrist, presets, screen, fireEvent, waitFor, actionsOf,\n * } from \"grist-widget-sdk/emulator/testing\"\n */\n\nexport { renderWithGrist } from \"./render-with-grist\"\nexport type {\n RenderWithGristOptions,\n RenderWithGristResult,\n} from \"./render-with-grist\"\n\nexport {\n actionsOf,\n eventsOf,\n waitForEvent,\n type WaitForEventKind,\n type WaitForEventOptions,\n} from \"./matchers\"\n\n// Document fixtures (re-export from the emulator entry for convenience).\nexport { presets } from \"../templates\"\n\n// @testing-library/react convenience re-exports — see apps/docs/work.md\nexport {\n act,\n cleanup,\n fireEvent,\n render,\n screen,\n waitFor,\n within,\n} from \"@testing-library/react\"\nexport type { RenderOptions, RenderResult } from \"@testing-library/react\"\n"],"mappings":";;;;;;;;;;;;;;;;AAaA,SAAS,cAAqD;AAmBvD,SAAS,gBACd,IACA,UAAkC,CAAC,GACZ;AAIvB,oBAAkB;AAElB,2BAAyB;AACzB,+BAA6B;AAC7B,wCAAsC;AAEtC,QAAM,WACJ,QAAQ,YACR,oBAAoB,QAAQ,YAAY,EAAE,UAAU,QAAQ,MAAM,EAAE,CAAC;AAEvE,QAAM,SAAS,OAAO,IAAI,OAAO;AACjC,QAAM,kBAAkB,OAAO;AAC/B,SAAO,UAAU,MAAM;AACrB,oBAAgB;AAChB,QAAI,CAAC,QAAQ,SAAU,UAAS,QAAQ;AACxC,iCAA6B;AAC7B,6BAAyB;AACzB,0CAAsC;AAAA,EACxC;AACA,SAAO,OAAO,OAAO,QAAQ,EAAE,SAAS,CAAC;AAC3C;AAEA,SAAS,oBAAoB;AAC3B,MAAI,OAAO,WAAW,YAAa;AACnC,MAAI,OAAO,SAAS,OAAO,IAAK;AAChC,MAAI;AACF,WAAO,eAAe,QAAQ,OAAO;AAAA,MACnC,cAAc;AAAA,MACd,KAAK,OAAO,CAAC;AAAA,IACf,CAAC;AAAA,EACH,QAAQ;AAAA,EAGR;AACF;;;AC5DO,SAAS,UAAU,UAAsC;AAC9D,SAAO,SAAS,IACb,QAAQ,EACR,OAAO,CAAC,MAA2E,EAAE,SAAS,aAAa,EAC3G,IAAI,CAAC,MAAM,EAAE,MAAmB;AACrC;AAEO,SAAS,SAAS,UAAmD;AAC1E,SAAO,SAAS,IAAI,QAAQ;AAC9B;AASA,SAAS,iBAAiB,MAA2D;AACnF,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,CAAC,MAAM,EAAE,SAAS;AAAA,IAC3B,KAAK;AAAA,IACL,KAAK;AACH,aAAO,CAAC,MAAM,EAAE,SAAS;AAAA,IAC3B,KAAK;AACH,aAAO,CAAC,MAAM,EAAE,SAAS;AAAA,IAC3B,KAAK;AACH,aAAO,CAAC,MAAM,EAAE,SAAS;AAAA,IAC3B,KAAK;AACH,aAAO,CAAC,MAAM,EAAE,SAAS;AAAA,EAC7B;AACF;AAEA,SAAS,iBACP,UACA,WACA,WACA,OACwB;AACxB,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,QAAQ,WAAW,MAAM;AAC7B,UAAI;AACJ,YAAM,OAAO,QAAQ,WAAW,KAAK,MAAM;AAC3C,YAAM,SAAS,SAAS,IACrB,QAAQ,EACR,MAAM,EAAE,EACR,IAAI,CAAC,MAAM,EAAE,IAAI,EACjB,KAAK,IAAI;AACZ;AAAA,QACE,IAAI;AAAA,UACF,gCAAgC,SAAS,KAAK,IAAI,OAC/C,SAAS,uBAAuB,MAAM,MAAM;AAAA,QACjD;AAAA,MACF;AAAA,IACF,GAAG,SAAS;AACZ,UAAM,MAAM,SAAS,IAAI,GAAG,CAAC,UAAU;AACrC,UAAI,UAAU,KAAK,GAAG;AACpB,qBAAa,KAAK;AAClB,YAAI;AACJ,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAYO,SAAS,aACd,UACA,iBACA,mBAAiD,KACzB;AACxB,MAAI,OAAO,oBAAoB,UAAU;AACvC,UAAM,OAAO,OAAO,qBAAqB,WAAW,mBAAmB,CAAC;AACxE,UAAMA,aAAY,KAAK,aAAa;AACpC,WAAO,iBAAiB,UAAU,iBAAiB,eAAe,GAAGA,YAAW,eAAe;AAAA,EACjG;AACA,QAAM,YAAY,OAAO,qBAAqB,WAAW,mBAAmB;AAC5E,SAAO,iBAAiB,UAAU,iBAAiB,SAAS;AAC9D;;;ACvEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;","names":["timeoutMs","render"]}