@thoughtspot/visual-embed-sdk 1.51.0 → 1.51.1

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 (212) hide show
  1. package/cjs/package.json +1 -1
  2. package/cjs/src/config.d.ts +1 -0
  3. package/cjs/src/config.d.ts.map +1 -1
  4. package/cjs/src/config.js +2 -1
  5. package/cjs/src/config.js.map +1 -1
  6. package/cjs/src/css-variables.d.ts +27 -0
  7. package/cjs/src/css-variables.d.ts.map +1 -1
  8. package/cjs/src/embed/app.d.ts +43 -12
  9. package/cjs/src/embed/app.d.ts.map +1 -1
  10. package/cjs/src/embed/app.js +29 -3
  11. package/cjs/src/embed/app.js.map +1 -1
  12. package/cjs/src/embed/app.spec.js +253 -1
  13. package/cjs/src/embed/app.spec.js.map +1 -1
  14. package/cjs/src/embed/base.d.ts +1 -1
  15. package/cjs/src/embed/base.js +1 -1
  16. package/cjs/src/embed/conversation.d.ts +171 -1
  17. package/cjs/src/embed/conversation.d.ts.map +1 -1
  18. package/cjs/src/embed/conversation.js +4 -2
  19. package/cjs/src/embed/conversation.js.map +1 -1
  20. package/cjs/src/embed/conversation.spec.js +125 -0
  21. package/cjs/src/embed/conversation.spec.js.map +1 -1
  22. package/cjs/src/embed/events.spec.js +3 -0
  23. package/cjs/src/embed/events.spec.js.map +1 -1
  24. package/cjs/src/embed/host-events.spec.js +36 -0
  25. package/cjs/src/embed/host-events.spec.js.map +1 -1
  26. package/cjs/src/embed/hostEventClient/host-event-client.d.ts +1 -1
  27. package/cjs/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  28. package/cjs/src/embed/hostEventClient/host-event-client.spec.js +18 -0
  29. package/cjs/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
  30. package/cjs/src/embed/liveboard.d.ts +40 -13
  31. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  32. package/cjs/src/embed/liveboard.js +25 -4
  33. package/cjs/src/embed/liveboard.js.map +1 -1
  34. package/cjs/src/embed/liveboard.spec.js +349 -2
  35. package/cjs/src/embed/liveboard.spec.js.map +1 -1
  36. package/cjs/src/embed/spotter-utils.d.ts +8 -1
  37. package/cjs/src/embed/spotter-utils.d.ts.map +1 -1
  38. package/cjs/src/embed/spotter-utils.js +15 -1
  39. package/cjs/src/embed/spotter-utils.js.map +1 -1
  40. package/cjs/src/embed/spotter-utils.spec.js +68 -0
  41. package/cjs/src/embed/spotter-utils.spec.js.map +1 -1
  42. package/cjs/src/embed/spotter-viz-utils.d.ts +9 -10
  43. package/cjs/src/embed/spotter-viz-utils.d.ts.map +1 -1
  44. package/cjs/src/embed/spotter-viz-utils.js.map +1 -1
  45. package/cjs/src/embed/ts-embed.d.ts +16 -8
  46. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  47. package/cjs/src/embed/ts-embed.js +69 -32
  48. package/cjs/src/embed/ts-embed.js.map +1 -1
  49. package/cjs/src/embed/ts-embed.spec.js +537 -3
  50. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  51. package/cjs/src/index.d.ts +3 -3
  52. package/cjs/src/index.d.ts.map +1 -1
  53. package/cjs/src/index.js +3 -2
  54. package/cjs/src/index.js.map +1 -1
  55. package/cjs/src/react/all-types-export.d.ts +1 -1
  56. package/cjs/src/react/all-types-export.d.ts.map +1 -1
  57. package/cjs/src/react/all-types-export.js +2 -1
  58. package/cjs/src/react/all-types-export.js.map +1 -1
  59. package/cjs/src/react/all-types-export.parity.spec.d.ts +2 -0
  60. package/cjs/src/react/all-types-export.parity.spec.d.ts.map +1 -0
  61. package/cjs/src/react/all-types-export.parity.spec.js +194 -0
  62. package/cjs/src/react/all-types-export.parity.spec.js.map +1 -0
  63. package/cjs/src/react/index.d.ts +66 -32
  64. package/cjs/src/react/index.d.ts.map +1 -1
  65. package/cjs/src/react/index.js +5 -3
  66. package/cjs/src/react/index.js.map +1 -1
  67. package/cjs/src/react/index.spec.js +101 -0
  68. package/cjs/src/react/index.spec.js.map +1 -1
  69. package/cjs/src/types.d.ts +330 -18
  70. package/cjs/src/types.d.ts.map +1 -1
  71. package/cjs/src/types.js +182 -12
  72. package/cjs/src/types.js.map +1 -1
  73. package/cjs/src/utils.spec.js +4 -0
  74. package/cjs/src/utils.spec.js.map +1 -1
  75. package/dist/{index-aBzH5ZwF.js → index-DxLfQxoT.js} +1 -1
  76. package/dist/src/config.d.ts +1 -0
  77. package/dist/src/config.d.ts.map +1 -1
  78. package/dist/src/css-variables.d.ts +27 -0
  79. package/dist/src/css-variables.d.ts.map +1 -1
  80. package/dist/src/embed/app.d.ts +43 -12
  81. package/dist/src/embed/app.d.ts.map +1 -1
  82. package/dist/src/embed/base.d.ts +1 -1
  83. package/dist/src/embed/conversation.d.ts +171 -1
  84. package/dist/src/embed/conversation.d.ts.map +1 -1
  85. package/dist/src/embed/hostEventClient/host-event-client.d.ts +1 -1
  86. package/dist/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  87. package/dist/src/embed/liveboard.d.ts +40 -13
  88. package/dist/src/embed/liveboard.d.ts.map +1 -1
  89. package/dist/src/embed/spotter-utils.d.ts +8 -1
  90. package/dist/src/embed/spotter-utils.d.ts.map +1 -1
  91. package/dist/src/embed/spotter-viz-utils.d.ts +9 -10
  92. package/dist/src/embed/spotter-viz-utils.d.ts.map +1 -1
  93. package/dist/src/embed/ts-embed.d.ts +16 -8
  94. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  95. package/dist/src/index.d.ts +3 -3
  96. package/dist/src/index.d.ts.map +1 -1
  97. package/dist/src/react/all-types-export.d.ts +1 -1
  98. package/dist/src/react/all-types-export.d.ts.map +1 -1
  99. package/dist/src/react/all-types-export.parity.spec.d.ts +2 -0
  100. package/dist/src/react/all-types-export.parity.spec.d.ts.map +1 -0
  101. package/dist/src/react/index.d.ts +66 -32
  102. package/dist/src/react/index.d.ts.map +1 -1
  103. package/dist/src/types.d.ts +330 -18
  104. package/dist/src/types.d.ts.map +1 -1
  105. package/dist/tsembed-react.es.js +330 -61
  106. package/dist/tsembed-react.js +329 -60
  107. package/dist/tsembed.es.js +390 -123
  108. package/dist/tsembed.js +392 -125
  109. package/dist/visual-embed-sdk-react-full.d.ts +696 -89
  110. package/dist/visual-embed-sdk-react.d.ts +692 -88
  111. package/dist/visual-embed-sdk.d.ts +632 -59
  112. package/lib/package.json +1 -1
  113. package/lib/src/config.d.ts +1 -0
  114. package/lib/src/config.d.ts.map +1 -1
  115. package/lib/src/config.js +1 -0
  116. package/lib/src/config.js.map +1 -1
  117. package/lib/src/css-variables.d.ts +27 -0
  118. package/lib/src/css-variables.d.ts.map +1 -1
  119. package/lib/src/embed/app.d.ts +43 -12
  120. package/lib/src/embed/app.d.ts.map +1 -1
  121. package/lib/src/embed/app.js +30 -4
  122. package/lib/src/embed/app.js.map +1 -1
  123. package/lib/src/embed/app.spec.js +253 -1
  124. package/lib/src/embed/app.spec.js.map +1 -1
  125. package/lib/src/embed/base.d.ts +1 -1
  126. package/lib/src/embed/base.js +1 -1
  127. package/lib/src/embed/conversation.d.ts +171 -1
  128. package/lib/src/embed/conversation.d.ts.map +1 -1
  129. package/lib/src/embed/conversation.js +5 -3
  130. package/lib/src/embed/conversation.js.map +1 -1
  131. package/lib/src/embed/conversation.spec.js +125 -0
  132. package/lib/src/embed/conversation.spec.js.map +1 -1
  133. package/lib/src/embed/events.spec.js +3 -0
  134. package/lib/src/embed/events.spec.js.map +1 -1
  135. package/lib/src/embed/host-events.spec.js +36 -0
  136. package/lib/src/embed/host-events.spec.js.map +1 -1
  137. package/lib/src/embed/hostEventClient/host-event-client.d.ts +1 -1
  138. package/lib/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  139. package/lib/src/embed/hostEventClient/host-event-client.spec.js +18 -0
  140. package/lib/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
  141. package/lib/src/embed/liveboard.d.ts +40 -13
  142. package/lib/src/embed/liveboard.d.ts.map +1 -1
  143. package/lib/src/embed/liveboard.js +25 -4
  144. package/lib/src/embed/liveboard.js.map +1 -1
  145. package/lib/src/embed/liveboard.spec.js +349 -2
  146. package/lib/src/embed/liveboard.spec.js.map +1 -1
  147. package/lib/src/embed/spotter-utils.d.ts +8 -1
  148. package/lib/src/embed/spotter-utils.d.ts.map +1 -1
  149. package/lib/src/embed/spotter-utils.js +13 -0
  150. package/lib/src/embed/spotter-utils.js.map +1 -1
  151. package/lib/src/embed/spotter-utils.spec.js +69 -1
  152. package/lib/src/embed/spotter-utils.spec.js.map +1 -1
  153. package/lib/src/embed/spotter-viz-utils.d.ts +9 -10
  154. package/lib/src/embed/spotter-viz-utils.d.ts.map +1 -1
  155. package/lib/src/embed/spotter-viz-utils.js.map +1 -1
  156. package/lib/src/embed/ts-embed.d.ts +16 -8
  157. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  158. package/lib/src/embed/ts-embed.js +69 -32
  159. package/lib/src/embed/ts-embed.js.map +1 -1
  160. package/lib/src/embed/ts-embed.spec.js +537 -3
  161. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  162. package/lib/src/index.d.ts +3 -3
  163. package/lib/src/index.d.ts.map +1 -1
  164. package/lib/src/index.js +2 -2
  165. package/lib/src/index.js.map +1 -1
  166. package/lib/src/react/all-types-export.d.ts +1 -1
  167. package/lib/src/react/all-types-export.d.ts.map +1 -1
  168. package/lib/src/react/all-types-export.js +1 -1
  169. package/lib/src/react/all-types-export.js.map +1 -1
  170. package/lib/src/react/all-types-export.parity.spec.d.ts +2 -0
  171. package/lib/src/react/all-types-export.parity.spec.d.ts.map +1 -0
  172. package/lib/src/react/all-types-export.parity.spec.js +191 -0
  173. package/lib/src/react/all-types-export.parity.spec.js.map +1 -0
  174. package/lib/src/react/index.d.ts +66 -32
  175. package/lib/src/react/index.d.ts.map +1 -1
  176. package/lib/src/react/index.js +5 -3
  177. package/lib/src/react/index.js.map +1 -1
  178. package/lib/src/react/index.spec.js +102 -1
  179. package/lib/src/react/index.spec.js.map +1 -1
  180. package/lib/src/types.d.ts +330 -18
  181. package/lib/src/types.d.ts.map +1 -1
  182. package/lib/src/types.js +182 -12
  183. package/lib/src/types.js.map +1 -1
  184. package/lib/src/utils.spec.js +4 -0
  185. package/lib/src/utils.spec.js.map +1 -1
  186. package/lib/src/visual-embed-sdk.d.ts +632 -59
  187. package/package.json +1 -1
  188. package/src/config.ts +1 -0
  189. package/src/css-variables.ts +32 -0
  190. package/src/embed/app.spec.ts +335 -1
  191. package/src/embed/app.ts +71 -13
  192. package/src/embed/base.ts +1 -1
  193. package/src/embed/conversation.spec.ts +136 -0
  194. package/src/embed/conversation.ts +181 -3
  195. package/src/embed/events.spec.ts +4 -0
  196. package/src/embed/host-events.spec.ts +48 -0
  197. package/src/embed/hostEventClient/host-event-client.spec.ts +34 -0
  198. package/src/embed/hostEventClient/host-event-client.ts +2 -2
  199. package/src/embed/liveboard.spec.ts +457 -2
  200. package/src/embed/liveboard.ts +74 -15
  201. package/src/embed/spotter-utils.spec.ts +72 -0
  202. package/src/embed/spotter-utils.ts +21 -1
  203. package/src/embed/spotter-viz-utils.ts +9 -10
  204. package/src/embed/ts-embed.spec.ts +699 -3
  205. package/src/embed/ts-embed.ts +83 -37
  206. package/src/index.ts +43 -2
  207. package/src/react/all-types-export.parity.spec.ts +208 -0
  208. package/src/react/all-types-export.ts +22 -0
  209. package/src/react/index.spec.tsx +158 -0
  210. package/src/react/index.tsx +70 -33
  211. package/src/types.ts +329 -16
  212. package/src/utils.spec.ts +5 -0
@@ -28,6 +28,7 @@ import * as auth from '../auth';
28
28
  import * as previewService from '../utils/graphql/preview-service';
29
29
  import * as SessionInfoService from '../utils/sessionInfoService';
30
30
  import { logger } from '../utils/logger';
31
+ import { DEFAULT_LAZY_LOADING_MARGIN } from '../config';
31
32
 
32
33
  const defaultViewConfig = {
33
34
  frameParams: {
@@ -156,6 +157,23 @@ describe('Liveboard/viz embed tests', () => {
156
157
  });
157
158
  });
158
159
 
160
+ test('should hide and disable the Spotter button on the Liveboard header', async () => {
161
+ expect(Action.SpotterOnLiveboard).toBe('spotterOnLiveboard');
162
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
163
+ hiddenActions: [Action.SpotterOnLiveboard],
164
+ disabledActions: [Action.SpotterOnLiveboard],
165
+ ...defaultViewConfig,
166
+ liveboardId,
167
+ } as LiveboardViewConfig);
168
+ liveboardEmbed.render();
169
+ await executeAfterWait(() => {
170
+ expectUrlToHaveParamsWithValues(getIFrameSrc(), {
171
+ hideAction: JSON.stringify([Action.ReportError, Action.SpotterOnLiveboard]),
172
+ disableAction: JSON.stringify([Action.SpotterOnLiveboard]),
173
+ });
174
+ });
175
+ });
176
+
159
177
  test('should set visible actions', async () => {
160
178
  const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
161
179
  visibleActions: [Action.DownloadAsCsv, Action.DownloadAsPdf, Action.DownloadAsXlsx],
@@ -200,6 +218,21 @@ describe('Liveboard/viz embed tests', () => {
200
218
  });
201
219
  });
202
220
 
221
+ test('should set isLiveboardAlwaysOn12ColLayout to true in url', async () => {
222
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
223
+ isLiveboardAlwaysOn12ColLayout: true,
224
+ ...defaultViewConfig,
225
+ liveboardId,
226
+ } as LiveboardViewConfig);
227
+ liveboardEmbed.render();
228
+ await executeAfterWait(() => {
229
+ expectUrlMatchesWithParams(
230
+ getIFrameSrc(),
231
+ `http://${thoughtSpotHost}/?embedApp=true${defaultParams}&isLiveboardAlwaysOn12ColLayout=true${prefixParams}#/embed/viz/${liveboardId}`,
232
+ );
233
+ });
234
+ });
235
+
203
236
  test('should set isLiveboardStylingAndGroupingEnabled to true in url (deprecated, use isLiveboardMasterpiecesEnabled)', async () => {
204
237
  const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
205
238
  isLiveboardStylingAndGroupingEnabled: true,
@@ -215,6 +248,36 @@ describe('Liveboard/viz embed tests', () => {
215
248
  });
216
249
  });
217
250
 
251
+ test('should set liveboardGutter in url', async () => {
252
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
253
+ ...defaultViewConfig,
254
+ liveboardId,
255
+ liveboardGutter: 8,
256
+ } as LiveboardViewConfig);
257
+ liveboardEmbed.render();
258
+ await executeAfterWait(() => {
259
+ expectUrlMatchesWithParams(
260
+ getIFrameSrc(),
261
+ `http://${thoughtSpotHost}/?embedApp=true${defaultParams}&liveboardGutter=8${prefixParams}#/embed/viz/${liveboardId}`,
262
+ );
263
+ });
264
+ });
265
+
266
+ test('should set liveboardGutter to 0 in url', async () => {
267
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
268
+ ...defaultViewConfig,
269
+ liveboardId,
270
+ liveboardGutter: 0,
271
+ } as LiveboardViewConfig);
272
+ liveboardEmbed.render();
273
+ await executeAfterWait(() => {
274
+ expectUrlMatchesWithParams(
275
+ getIFrameSrc(),
276
+ `http://${thoughtSpotHost}/?embedApp=true${defaultParams}&liveboardGutter=0${prefixParams}#/embed/viz/${liveboardId}`,
277
+ );
278
+ });
279
+ });
280
+
218
281
  test('should set isThisPeriodInDateFiltersEnabled to true in url', async () => {
219
282
  const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
220
283
  ...defaultViewConfig,
@@ -1271,6 +1334,44 @@ describe('Liveboard/viz embed tests', () => {
1271
1334
  expect(onSpy).toHaveBeenCalledWith(HostEvent.Navigate, 'embed/viz/lb1/viz1');
1272
1335
  }, 1002);
1273
1336
  });
1337
+
1338
+ test('navigateToLiveboard should call preRender when preRenderConfig.preRenderId is set', () => {
1339
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1340
+ ...defaultViewConfig,
1341
+ liveboardId,
1342
+ preRenderConfig: {
1343
+ preRenderId: 'config-pre-render-id',
1344
+ },
1345
+ } as LiveboardViewConfig);
1346
+
1347
+ const preRenderSpy = jest
1348
+ .spyOn(liveboardEmbed, 'preRender')
1349
+ .mockResolvedValue(liveboardEmbed);
1350
+ liveboardEmbed.navigateToLiveboard('lb1');
1351
+
1352
+ expect(preRenderSpy).toHaveBeenCalledTimes(1);
1353
+ expect(preRenderSpy).toHaveBeenCalledWith(true);
1354
+ });
1355
+
1356
+ test('navigateToLiveboard should prefer preRenderConfig.preRenderId over top-level preRenderId', () => {
1357
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1358
+ ...defaultViewConfig,
1359
+ liveboardId,
1360
+ preRenderId: 'top-level-id',
1361
+ preRenderConfig: {
1362
+ preRenderId: 'config-level-id',
1363
+ },
1364
+ } as LiveboardViewConfig);
1365
+
1366
+ const preRenderSpy = jest
1367
+ .spyOn(liveboardEmbed, 'preRender')
1368
+ .mockResolvedValue(liveboardEmbed);
1369
+ liveboardEmbed.navigateToLiveboard('lb1');
1370
+
1371
+ expect(preRenderSpy).toHaveBeenCalledTimes(1);
1372
+ expect(preRenderSpy).toHaveBeenCalledWith(true);
1373
+ });
1374
+
1274
1375
  test('should set runtime parametere values in url params', async () => {
1275
1376
  const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1276
1377
  ...defaultViewConfig,
@@ -1337,6 +1438,46 @@ describe('Liveboard/viz embed tests', () => {
1337
1438
  });
1338
1439
  });
1339
1440
 
1441
+ test('should set openSpotterOnLiveboardByDefault parameter in url params', async () => {
1442
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1443
+ ...defaultViewConfig,
1444
+ liveboardId,
1445
+ spotterChatConfig: { openSpotterOnLiveboardByDefault: true },
1446
+ } as LiveboardViewConfig);
1447
+ await liveboardEmbed.render();
1448
+ await executeAfterWait(() => {
1449
+ expectUrlToHaveParamsWithValues(getIFrameSrc(), {
1450
+ openSpotterOnLiveboardByDefault: 'true',
1451
+ });
1452
+ });
1453
+ });
1454
+
1455
+ test('should set openSpotterOnLiveboardByDefault to false when explicitly disabled', async () => {
1456
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1457
+ ...defaultViewConfig,
1458
+ liveboardId,
1459
+ spotterChatConfig: { openSpotterOnLiveboardByDefault: false },
1460
+ } as LiveboardViewConfig);
1461
+ await liveboardEmbed.render();
1462
+ await executeAfterWait(() => {
1463
+ expectUrlToHaveParamsWithValues(getIFrameSrc(), {
1464
+ openSpotterOnLiveboardByDefault: 'false',
1465
+ });
1466
+ });
1467
+ });
1468
+
1469
+ test('should not set openSpotterOnLiveboardByDefault when it is not configured', async () => {
1470
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1471
+ ...defaultViewConfig,
1472
+ liveboardId,
1473
+ spotterChatConfig: { enableStarterPrompts: true },
1474
+ } as LiveboardViewConfig);
1475
+ await liveboardEmbed.render();
1476
+ await executeAfterWait(() => {
1477
+ expect(getIFrameSrc()).not.toContain('openSpotterOnLiveboardByDefault');
1478
+ });
1479
+ });
1480
+
1340
1481
  test('should set spotterFileUploadFileTypes parameter in url params', async () => {
1341
1482
  const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1342
1483
  ...defaultViewConfig,
@@ -1480,6 +1621,37 @@ describe('Liveboard/viz embed tests', () => {
1480
1621
  });
1481
1622
  });
1482
1623
 
1624
+ test('should include starterPrompts in APP_INIT embedParams', async () => {
1625
+ const starterPrompts = {
1626
+ enable: true,
1627
+ quick: {
1628
+ label: 'Quick',
1629
+ questions: [
1630
+ { label: 'Q1', prompt: 'P1' },
1631
+ { label: 'Q2', prompt: 'P2' },
1632
+ ],
1633
+ },
1634
+ research: { label: 'Research' },
1635
+ };
1636
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1637
+ ...defaultViewConfig,
1638
+ liveboardId,
1639
+ spotterChatConfig: { starterPrompts },
1640
+ } as LiveboardViewConfig);
1641
+
1642
+ mockMessageChannel();
1643
+ await liveboardEmbed.render();
1644
+
1645
+ const mockPort: any = { postMessage: jest.fn() };
1646
+ await executeAfterWait(() => {
1647
+ postMessageToParent(getIFrameEl().contentWindow, { type: EmbedEvent.APP_INIT, data: {} }, mockPort);
1648
+ });
1649
+ await executeAfterWait(() => {
1650
+ expect(mockPort.postMessage.mock.calls[0][0].data.embedParams.starterPrompts)
1651
+ .toEqual(starterPrompts);
1652
+ });
1653
+ });
1654
+
1483
1655
  test('should pass brandHeadline through spotterVizConfig in APP_INIT', async () => {
1484
1656
  const spotterViz = { brandName: 'MyBrand', brandHeadline: "Hi, there! I'm" };
1485
1657
  const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
@@ -1880,6 +2052,255 @@ describe('Liveboard/viz embed tests', () => {
1880
2052
  }, 100);
1881
2053
  });
1882
2054
 
2055
+ test('should default lazy loading flags to true when fullHeight is enabled', async () => {
2056
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
2057
+ ...defaultViewConfig,
2058
+ liveboardId,
2059
+ fullHeight: true,
2060
+ } as LiveboardViewConfig);
2061
+
2062
+ expect((liveboardEmbed as any).viewConfig.lazyLoadingForFullHeight).toBe(true);
2063
+ expect(
2064
+ (liveboardEmbed as any).viewConfig.enableScrollableContainerLazyLoading,
2065
+ ).toBe(true);
2066
+ expect((liveboardEmbed as any).viewConfig.lazyLoadingMargin).toBe('500px 0px');
2067
+
2068
+ await liveboardEmbed.render();
2069
+
2070
+ await executeAfterWait(() => {
2071
+ const iframeSrc = getIFrameSrc();
2072
+ expect(iframeSrc).toContain('isLazyLoadingForEmbedEnabled=true');
2073
+ expect(iframeSrc).toContain('isFullHeightPinboard=true');
2074
+ expect(iframeSrc).toContain('rootMarginForLazyLoad=500px%200px');
2075
+ }, 100);
2076
+ });
2077
+
2078
+ test('should not default lazy loading flags when fullHeight is not enabled', async () => {
2079
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
2080
+ ...defaultViewConfig,
2081
+ liveboardId,
2082
+ } as LiveboardViewConfig);
2083
+
2084
+ expect((liveboardEmbed as any).viewConfig.lazyLoadingForFullHeight).toBeUndefined();
2085
+ expect(
2086
+ (liveboardEmbed as any).viewConfig.enableScrollableContainerLazyLoading,
2087
+ ).toBeUndefined();
2088
+ expect((liveboardEmbed as any).viewConfig.lazyLoadingMargin).toBeUndefined();
2089
+ });
2090
+
2091
+ test('should not write the defaults back onto the caller view config', async () => {
2092
+ const callerViewConfig = {
2093
+ ...defaultViewConfig,
2094
+ liveboardId,
2095
+ fullHeight: true,
2096
+ } as LiveboardViewConfig;
2097
+
2098
+ new LiveboardEmbed(getRootEl(), callerViewConfig);
2099
+
2100
+ expect(callerViewConfig.lazyLoadingForFullHeight).toBeUndefined();
2101
+ expect(callerViewConfig.enableScrollableContainerLazyLoading).toBeUndefined();
2102
+ expect(callerViewConfig.lazyLoadingMargin).toBeUndefined();
2103
+ });
2104
+
2105
+ test('should not default lazy loading flags when fullHeight is explicitly false', async () => {
2106
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
2107
+ ...defaultViewConfig,
2108
+ liveboardId,
2109
+ fullHeight: false,
2110
+ } as LiveboardViewConfig);
2111
+
2112
+ expect((liveboardEmbed as any).viewConfig.lazyLoadingForFullHeight).toBeUndefined();
2113
+ expect(
2114
+ (liveboardEmbed as any).viewConfig.enableScrollableContainerLazyLoading,
2115
+ ).toBeUndefined();
2116
+ expect((liveboardEmbed as any).viewConfig.lazyLoadingMargin).toBeUndefined();
2117
+ });
2118
+
2119
+ test('should default lazyLoadingMargin when lazyLoadingForFullHeight is set explicitly', async () => {
2120
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
2121
+ ...defaultViewConfig,
2122
+ liveboardId,
2123
+ fullHeight: true,
2124
+ lazyLoadingForFullHeight: true,
2125
+ } as LiveboardViewConfig);
2126
+
2127
+ expect((liveboardEmbed as any).viewConfig.lazyLoadingMargin).toBe(
2128
+ DEFAULT_LAZY_LOADING_MARGIN,
2129
+ );
2130
+
2131
+ await liveboardEmbed.render();
2132
+
2133
+ await executeAfterWait(() => {
2134
+ expect(getIFrameSrc()).toContain('rootMarginForLazyLoad=500px%200px');
2135
+ }, 100);
2136
+ });
2137
+
2138
+ test('should let an explicit lazyLoadingMargin win over the default', async () => {
2139
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
2140
+ ...defaultViewConfig,
2141
+ liveboardId,
2142
+ fullHeight: true,
2143
+ lazyLoadingMargin: '250px',
2144
+ } as LiveboardViewConfig);
2145
+
2146
+ await liveboardEmbed.render();
2147
+
2148
+ await executeAfterWait(() => {
2149
+ const iframeSrc = getIFrameSrc();
2150
+ expect(iframeSrc).toContain('rootMarginForLazyLoad=250px');
2151
+ expect(iframeSrc).not.toContain('rootMarginForLazyLoad=500px%200px');
2152
+ }, 100);
2153
+ });
2154
+
2155
+ test('should drop an invalid lazyLoadingMargin and log an error', async () => {
2156
+ const loggerErrorSpy = jest.spyOn(logger, 'error').mockImplementation(() => undefined);
2157
+
2158
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
2159
+ ...defaultViewConfig,
2160
+ liveboardId,
2161
+ fullHeight: true,
2162
+ lazyLoadingMargin: 'not-a-margin',
2163
+ } as LiveboardViewConfig);
2164
+
2165
+ await liveboardEmbed.render();
2166
+
2167
+ await executeAfterWait(() => {
2168
+ const iframeSrc = getIFrameSrc();
2169
+ expect(iframeSrc).toContain('isLazyLoadingForEmbedEnabled=true');
2170
+ expect(iframeSrc).not.toContain('rootMarginForLazyLoad');
2171
+ expect(loggerErrorSpy).toHaveBeenCalledWith(
2172
+ 'Please provide a valid lazyLoadingMargin value (e.g., "10px")',
2173
+ );
2174
+ }, 100);
2175
+ });
2176
+
2177
+ test('should track scrollable ancestors by default when only fullHeight is set', async () => {
2178
+ const scrollContainer = getRootEl();
2179
+ scrollContainer.style.overflow = 'auto';
2180
+
2181
+ const scrollContainerSpy = jest.spyOn(scrollContainer, 'addEventListener');
2182
+ const resizeObserveSpy = jest.fn();
2183
+ const resizeDisconnectSpy = jest.fn();
2184
+ const originalResizeObserver = (window as any).ResizeObserver;
2185
+ (window as any).ResizeObserver = jest.fn().mockImplementation(() => ({
2186
+ observe: resizeObserveSpy,
2187
+ disconnect: resizeDisconnectSpy,
2188
+ }));
2189
+
2190
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
2191
+ ...defaultViewConfig,
2192
+ liveboardId,
2193
+ fullHeight: true,
2194
+ } as LiveboardViewConfig);
2195
+
2196
+ await liveboardEmbed.render();
2197
+
2198
+ await executeAfterWait(() => {
2199
+ expect(scrollContainerSpy).toHaveBeenCalledWith('scroll', expect.any(Function));
2200
+ expect(resizeObserveSpy).toHaveBeenCalledWith(scrollContainer);
2201
+ }, 100);
2202
+
2203
+ liveboardEmbed.destroy();
2204
+ expect(resizeDisconnectSpy).toHaveBeenCalled();
2205
+
2206
+ scrollContainer.style.overflow = '';
2207
+ (window as any).ResizeObserver = originalResizeObserver;
2208
+ });
2209
+
2210
+ test('should skip ancestor tracking when enableScrollableContainerLazyLoading is false', async () => {
2211
+ const scrollContainer = getRootEl();
2212
+ scrollContainer.style.overflow = 'auto';
2213
+
2214
+ const scrollContainerSpy = jest.spyOn(scrollContainer, 'addEventListener');
2215
+ const windowSpy = jest.spyOn(window, 'addEventListener');
2216
+
2217
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
2218
+ ...defaultViewConfig,
2219
+ liveboardId,
2220
+ fullHeight: true,
2221
+ enableScrollableContainerLazyLoading: false,
2222
+ } as LiveboardViewConfig);
2223
+
2224
+ await liveboardEmbed.render();
2225
+
2226
+ await executeAfterWait(() => {
2227
+ expect(windowSpy).toHaveBeenCalledWith('scroll', expect.anything(), true);
2228
+ expect(scrollContainerSpy).not.toHaveBeenCalledWith('scroll', expect.any(Function));
2229
+ }, 100);
2230
+
2231
+ liveboardEmbed.destroy();
2232
+ scrollContainer.style.overflow = '';
2233
+ });
2234
+
2235
+ test('should wire the window scroll listener to the coordinates sender by default', async () => {
2236
+ const addEventListenerSpy = jest.spyOn(window, 'addEventListener');
2237
+
2238
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
2239
+ ...defaultViewConfig,
2240
+ liveboardId,
2241
+ fullHeight: true,
2242
+ } as LiveboardViewConfig);
2243
+
2244
+ const mockTrigger = jest
2245
+ .spyOn(liveboardEmbed, 'trigger')
2246
+ .mockImplementation(() => Promise.resolve(undefined as any));
2247
+
2248
+ await liveboardEmbed.render();
2249
+
2250
+ await executeAfterWait(() => {
2251
+ const scrollCall = addEventListenerSpy.mock.calls.find(
2252
+ ([eventName, , capture]) => eventName === 'scroll' && capture === true,
2253
+ );
2254
+ expect(scrollCall).toBeDefined();
2255
+
2256
+ // Call the handler the way a real scroll event would.
2257
+ (scrollCall as any)[1]();
2258
+
2259
+ expect(mockTrigger).toHaveBeenCalledWith(
2260
+ HostEvent.VisibleEmbedCoordinates,
2261
+ expect.objectContaining({ top: expect.any(Number) }),
2262
+ );
2263
+ }, 100);
2264
+
2265
+ liveboardEmbed.destroy();
2266
+ addEventListenerSpy.mockRestore();
2267
+ });
2268
+
2269
+ test('should remove listeners on destroy when the flags come from defaults', async () => {
2270
+ const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener');
2271
+
2272
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
2273
+ ...defaultViewConfig,
2274
+ liveboardId,
2275
+ fullHeight: true,
2276
+ } as LiveboardViewConfig);
2277
+
2278
+ await liveboardEmbed.render();
2279
+ liveboardEmbed.destroy();
2280
+
2281
+ expect(removeEventListenerSpy).toHaveBeenCalledWith('resize', expect.anything());
2282
+ expect(removeEventListenerSpy).toHaveBeenCalledWith('scroll', expect.anything(), true);
2283
+
2284
+ removeEventListenerSpy.mockRestore();
2285
+ });
2286
+
2287
+ test('should keep an explicit false for the lazy loading flags', async () => {
2288
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
2289
+ ...defaultViewConfig,
2290
+ liveboardId,
2291
+ fullHeight: true,
2292
+ lazyLoadingForFullHeight: false,
2293
+ enableScrollableContainerLazyLoading: false,
2294
+ lazyLoadingMargin: '0px',
2295
+ } as LiveboardViewConfig);
2296
+
2297
+ expect((liveboardEmbed as any).viewConfig.lazyLoadingForFullHeight).toBe(false);
2298
+ expect(
2299
+ (liveboardEmbed as any).viewConfig.enableScrollableContainerLazyLoading,
2300
+ ).toBe(false);
2301
+ expect((liveboardEmbed as any).viewConfig.lazyLoadingMargin).toBe('0px');
2302
+ });
2303
+
1883
2304
  test('should not set lazyLoadingForEmbed when lazyLoadingForFullHeight is enabled but fullHeight is false', async () => {
1884
2305
  const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1885
2306
  ...defaultViewConfig,
@@ -1959,7 +2380,7 @@ describe('Liveboard/viz embed tests', () => {
1959
2380
  });
1960
2381
  });
1961
2382
 
1962
- test('should send correct visible data when RequestVisibleEmbedCoordinates is triggered', async () => {
2383
+ test('should send visible data when fullHeight is enabled and lazyLoadingForFullHeight is omitted', async () => {
1963
2384
  const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1964
2385
  ...defaultViewConfig,
1965
2386
  liveboardId,
@@ -1974,7 +2395,7 @@ describe('Liveboard/viz embed tests', () => {
1974
2395
  // Trigger the lazy load data calculation
1975
2396
  (liveboardEmbed as any).sendFullHeightLazyLoadData();
1976
2397
 
1977
- expect(mockTrigger).not.toHaveBeenCalledWith(HostEvent.VisibleEmbedCoordinates, {
2398
+ expect(mockTrigger).toHaveBeenCalledWith(HostEvent.VisibleEmbedCoordinates, {
1978
2399
  top: 0,
1979
2400
  height: 500,
1980
2401
  left: 0,
@@ -2411,4 +2832,38 @@ describe('LiveboardEmbed updatedSpotterExperience tests', () => {
2411
2832
  );
2412
2833
  });
2413
2834
  });
2835
+
2836
+ describe('trigger typing contracts (compile-time)', () => {
2837
+ // Enforced by ts-jest at compile time. LiveboardEmbed OVERRIDES
2838
+ // TsEmbed.trigger, so its generic defaults must be asserted separately —
2839
+ // a bare `PayloadT` on the override silently collapses no-payload
2840
+ // responses back to `any` for every LiveboardEmbed consumer.
2841
+ type IsAny<T> = 0 extends 1 & T ? true : false;
2842
+ type Extends<A, B> = A extends B ? true : false;
2843
+
2844
+ it('infers typed response for no-payload calls through the LiveboardEmbed override', () => {
2845
+ const embed = new LiveboardEmbed(getRootEl(), {
2846
+ liveboardId: 'test-liveboard-id',
2847
+ } as LiveboardViewConfig);
2848
+
2849
+ const getTabs = () => embed.trigger(HostEvent.GetTabs);
2850
+ type TabsResponse = Awaited<ReturnType<typeof getTabs>>;
2851
+ const tabsIsTyped: IsAny<TabsResponse> = false;
2852
+ const tabsHasContractFields: Extends<
2853
+ TabsResponse,
2854
+ { numberOfTabs: number; orderedTabIds: string[] }
2855
+ > = true;
2856
+ const probeUnknownField = (r: TabsResponse) =>
2857
+ // @ts-expect-error — field not on the GetTabs contract (was silent when `any`)
2858
+ r.tabCount;
2859
+ void probeUnknownField;
2860
+
2861
+ const reload = () => embed.trigger(HostEvent.Reload);
2862
+ type ReloadResponse = Awaited<ReturnType<typeof reload>>;
2863
+ const reloadStaysAny: IsAny<ReloadResponse> = true;
2864
+
2865
+ expect([tabsIsTyped, tabsHasContractFields, reloadStaysAny])
2866
+ .toEqual([false, true, true]);
2867
+ });
2868
+ });
2414
2869
  });