@thoughtspot/visual-embed-sdk 1.52.1 → 1.52.2-beta.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 (217) hide show
  1. package/cjs/package.json +10 -3
  2. package/cjs/src/contracts/host-event-contracts.d.ts +49 -11
  3. package/cjs/src/contracts/host-event-contracts.d.ts.map +1 -1
  4. package/cjs/src/embed/app.d.ts +4 -3
  5. package/cjs/src/embed/app.d.ts.map +1 -1
  6. package/cjs/src/embed/app.js.map +1 -1
  7. package/cjs/src/embed/app.spec.js +10 -0
  8. package/cjs/src/embed/app.spec.js.map +1 -1
  9. package/cjs/src/embed/auto-frame-renderer.spec.js +24 -13
  10. package/cjs/src/embed/auto-frame-renderer.spec.js.map +1 -1
  11. package/cjs/src/embed/conversation.d.ts +1 -0
  12. package/cjs/src/embed/conversation.d.ts.map +1 -1
  13. package/cjs/src/embed/conversation.js +3 -3
  14. package/cjs/src/embed/conversation.js.map +1 -1
  15. package/cjs/src/embed/conversation.spec.js +35 -24
  16. package/cjs/src/embed/conversation.spec.js.map +1 -1
  17. package/cjs/src/embed/embedConfig.spec.js +2 -1
  18. package/cjs/src/embed/embedConfig.spec.js.map +1 -1
  19. package/cjs/src/embed/hostEventClient/host-event-client.d.ts +2 -2
  20. package/cjs/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  21. package/cjs/src/embed/hostEventClient/host-event-client.js +16 -10
  22. package/cjs/src/embed/hostEventClient/host-event-client.js.map +1 -1
  23. package/cjs/src/embed/hostEventClient/host-event-client.spec.js +14 -8
  24. package/cjs/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
  25. package/cjs/src/embed/hostEventClient/utils.d.ts +9 -0
  26. package/cjs/src/embed/hostEventClient/utils.d.ts.map +1 -1
  27. package/cjs/src/embed/hostEventClient/utils.js +49 -3
  28. package/cjs/src/embed/hostEventClient/utils.js.map +1 -1
  29. package/cjs/src/embed/hostEventClient/utils.spec.js +151 -0
  30. package/cjs/src/embed/hostEventClient/utils.spec.js.map +1 -1
  31. package/cjs/src/embed/liveboard.d.ts +4 -3
  32. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  33. package/cjs/src/embed/liveboard.js +17 -12
  34. package/cjs/src/embed/liveboard.js.map +1 -1
  35. package/cjs/src/embed/liveboard.spec.js +2 -1
  36. package/cjs/src/embed/liveboard.spec.js.map +1 -1
  37. package/cjs/src/embed/spotter-utils.d.ts +8 -1
  38. package/cjs/src/embed/spotter-utils.d.ts.map +1 -1
  39. package/cjs/src/embed/spotter-utils.js +14 -1
  40. package/cjs/src/embed/spotter-utils.js.map +1 -1
  41. package/cjs/src/embed/spotter-utils.spec.js +26 -0
  42. package/cjs/src/embed/spotter-utils.spec.js.map +1 -1
  43. package/cjs/src/embed/ts-embed.d.ts +35 -10
  44. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  45. package/cjs/src/embed/ts-embed.js +108 -73
  46. package/cjs/src/embed/ts-embed.js.map +1 -1
  47. package/cjs/src/embed/ts-embed.spec.d.ts.map +1 -1
  48. package/cjs/src/embed/ts-embed.spec.js +203 -30
  49. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  50. package/cjs/src/errors.js +1 -1
  51. package/cjs/src/errors.js.map +1 -1
  52. package/cjs/src/index.d.ts +2 -1
  53. package/cjs/src/index.d.ts.map +1 -1
  54. package/cjs/src/index.js +3 -1
  55. package/cjs/src/index.js.map +1 -1
  56. package/cjs/src/react/all-types-export.d.ts +1 -1
  57. package/cjs/src/react/all-types-export.d.ts.map +1 -1
  58. package/cjs/src/react/all-types-export.js +2 -1
  59. package/cjs/src/react/all-types-export.js.map +1 -1
  60. package/cjs/src/types.d.ts +45 -22
  61. package/cjs/src/types.d.ts.map +1 -1
  62. package/cjs/src/types.js +44 -21
  63. package/cjs/src/types.js.map +1 -1
  64. package/cjs/src/utils/authService/tokenizedAuthService.spec.js +2 -1
  65. package/cjs/src/utils/authService/tokenizedAuthService.spec.js.map +1 -1
  66. package/cjs/src/utils/custom-actions.spec.js +4 -2
  67. package/cjs/src/utils/custom-actions.spec.js.map +1 -1
  68. package/cjs/src/utils/filterConverter.d.ts +106 -0
  69. package/cjs/src/utils/filterConverter.d.ts.map +1 -0
  70. package/cjs/src/utils/filterConverter.js +215 -0
  71. package/cjs/src/utils/filterConverter.js.map +1 -0
  72. package/cjs/src/utils/filterConverter.spec.d.ts +2 -0
  73. package/cjs/src/utils/filterConverter.spec.d.ts.map +1 -0
  74. package/cjs/src/utils/filterConverter.spec.js +709 -0
  75. package/cjs/src/utils/filterConverter.spec.js.map +1 -0
  76. package/cjs/src/utils/processData.spec.js +2 -1
  77. package/cjs/src/utils/processData.spec.js.map +1 -1
  78. package/dist/{index-C1pmYb5H.js → index-BrJMbt8r.js} +1 -1
  79. package/dist/src/contracts/host-event-contracts.d.ts +49 -11
  80. package/dist/src/contracts/host-event-contracts.d.ts.map +1 -1
  81. package/dist/src/embed/app.d.ts +4 -3
  82. package/dist/src/embed/app.d.ts.map +1 -1
  83. package/dist/src/embed/conversation.d.ts +1 -0
  84. package/dist/src/embed/conversation.d.ts.map +1 -1
  85. package/dist/src/embed/hostEventClient/host-event-client.d.ts +2 -2
  86. package/dist/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  87. package/dist/src/embed/hostEventClient/utils.d.ts +9 -0
  88. package/dist/src/embed/hostEventClient/utils.d.ts.map +1 -1
  89. package/dist/src/embed/liveboard.d.ts +4 -3
  90. package/dist/src/embed/liveboard.d.ts.map +1 -1
  91. package/dist/src/embed/spotter-utils.d.ts +8 -1
  92. package/dist/src/embed/spotter-utils.d.ts.map +1 -1
  93. package/dist/src/embed/ts-embed.d.ts +35 -10
  94. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  95. package/dist/src/embed/ts-embed.spec.d.ts.map +1 -1
  96. package/dist/src/index.d.ts +2 -1
  97. package/dist/src/index.d.ts.map +1 -1
  98. package/dist/src/react/all-types-export.d.ts +1 -1
  99. package/dist/src/react/all-types-export.d.ts.map +1 -1
  100. package/dist/src/types.d.ts +45 -22
  101. package/dist/src/types.d.ts.map +1 -1
  102. package/dist/src/utils/filterConverter.d.ts +106 -0
  103. package/dist/src/utils/filterConverter.d.ts.map +1 -0
  104. package/dist/src/utils/filterConverter.spec.d.ts +2 -0
  105. package/dist/src/utils/filterConverter.spec.d.ts.map +1 -0
  106. package/dist/tsembed-react.es.js +252 -126
  107. package/dist/tsembed-react.js +251 -125
  108. package/dist/tsembed.es.js +460 -127
  109. package/dist/tsembed.js +459 -125
  110. package/dist/visual-embed-sdk-react-full.d.ts +233 -49
  111. package/dist/visual-embed-sdk-react.d.ts +139 -49
  112. package/dist/visual-embed-sdk.d.ts +233 -49
  113. package/lib/package.json +10 -3
  114. package/lib/src/contracts/host-event-contracts.d.ts +49 -11
  115. package/lib/src/contracts/host-event-contracts.d.ts.map +1 -1
  116. package/lib/src/embed/app.d.ts +4 -3
  117. package/lib/src/embed/app.d.ts.map +1 -1
  118. package/lib/src/embed/app.js.map +1 -1
  119. package/lib/src/embed/app.spec.js +10 -0
  120. package/lib/src/embed/app.spec.js.map +1 -1
  121. package/lib/src/embed/auto-frame-renderer.spec.js +24 -13
  122. package/lib/src/embed/auto-frame-renderer.spec.js.map +1 -1
  123. package/lib/src/embed/conversation.d.ts +1 -0
  124. package/lib/src/embed/conversation.d.ts.map +1 -1
  125. package/lib/src/embed/conversation.js +4 -4
  126. package/lib/src/embed/conversation.js.map +1 -1
  127. package/lib/src/embed/conversation.spec.js +35 -24
  128. package/lib/src/embed/conversation.spec.js.map +1 -1
  129. package/lib/src/embed/embedConfig.spec.js +2 -1
  130. package/lib/src/embed/embedConfig.spec.js.map +1 -1
  131. package/lib/src/embed/hostEventClient/host-event-client.d.ts +2 -2
  132. package/lib/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  133. package/lib/src/embed/hostEventClient/host-event-client.js +17 -11
  134. package/lib/src/embed/hostEventClient/host-event-client.js.map +1 -1
  135. package/lib/src/embed/hostEventClient/host-event-client.spec.js +14 -8
  136. package/lib/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
  137. package/lib/src/embed/hostEventClient/utils.d.ts +9 -0
  138. package/lib/src/embed/hostEventClient/utils.d.ts.map +1 -1
  139. package/lib/src/embed/hostEventClient/utils.js +47 -2
  140. package/lib/src/embed/hostEventClient/utils.js.map +1 -1
  141. package/lib/src/embed/hostEventClient/utils.spec.js +152 -1
  142. package/lib/src/embed/hostEventClient/utils.spec.js.map +1 -1
  143. package/lib/src/embed/liveboard.d.ts +4 -3
  144. package/lib/src/embed/liveboard.d.ts.map +1 -1
  145. package/lib/src/embed/liveboard.js +17 -12
  146. package/lib/src/embed/liveboard.js.map +1 -1
  147. package/lib/src/embed/liveboard.spec.js +2 -1
  148. package/lib/src/embed/liveboard.spec.js.map +1 -1
  149. package/lib/src/embed/spotter-utils.d.ts +8 -1
  150. package/lib/src/embed/spotter-utils.d.ts.map +1 -1
  151. package/lib/src/embed/spotter-utils.js +12 -0
  152. package/lib/src/embed/spotter-utils.js.map +1 -1
  153. package/lib/src/embed/spotter-utils.spec.js +27 -1
  154. package/lib/src/embed/spotter-utils.spec.js.map +1 -1
  155. package/lib/src/embed/ts-embed.d.ts +35 -10
  156. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  157. package/lib/src/embed/ts-embed.js +109 -74
  158. package/lib/src/embed/ts-embed.js.map +1 -1
  159. package/lib/src/embed/ts-embed.spec.d.ts.map +1 -1
  160. package/lib/src/embed/ts-embed.spec.js +203 -30
  161. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  162. package/lib/src/errors.js +1 -1
  163. package/lib/src/errors.js.map +1 -1
  164. package/lib/src/index.d.ts +2 -1
  165. package/lib/src/index.d.ts.map +1 -1
  166. package/lib/src/index.js +2 -1
  167. package/lib/src/index.js.map +1 -1
  168. package/lib/src/react/all-types-export.d.ts +1 -1
  169. package/lib/src/react/all-types-export.d.ts.map +1 -1
  170. package/lib/src/react/all-types-export.js +1 -1
  171. package/lib/src/react/all-types-export.js.map +1 -1
  172. package/lib/src/types.d.ts +45 -22
  173. package/lib/src/types.d.ts.map +1 -1
  174. package/lib/src/types.js +44 -21
  175. package/lib/src/types.js.map +1 -1
  176. package/lib/src/utils/authService/tokenizedAuthService.spec.js +2 -1
  177. package/lib/src/utils/authService/tokenizedAuthService.spec.js.map +1 -1
  178. package/lib/src/utils/custom-actions.spec.js +4 -2
  179. package/lib/src/utils/custom-actions.spec.js.map +1 -1
  180. package/lib/src/utils/filterConverter.d.ts +106 -0
  181. package/lib/src/utils/filterConverter.d.ts.map +1 -0
  182. package/lib/src/utils/filterConverter.js +210 -0
  183. package/lib/src/utils/filterConverter.js.map +1 -0
  184. package/lib/src/utils/filterConverter.spec.d.ts +2 -0
  185. package/lib/src/utils/filterConverter.spec.d.ts.map +1 -0
  186. package/lib/src/utils/filterConverter.spec.js +707 -0
  187. package/lib/src/utils/filterConverter.spec.js.map +1 -0
  188. package/lib/src/utils/processData.spec.js +2 -1
  189. package/lib/src/utils/processData.spec.js.map +1 -1
  190. package/lib/src/visual-embed-sdk.d.ts +233 -49
  191. package/package.json +10 -3
  192. package/src/contracts/host-event-contracts.ts +55 -11
  193. package/src/embed/app.spec.ts +15 -0
  194. package/src/embed/app.ts +4 -3
  195. package/src/embed/auto-frame-renderer.spec.ts +24 -13
  196. package/src/embed/conversation.spec.ts +38 -27
  197. package/src/embed/conversation.ts +4 -4
  198. package/src/embed/embedConfig.spec.ts +2 -1
  199. package/src/embed/hostEventClient/host-event-client.spec.ts +14 -8
  200. package/src/embed/hostEventClient/host-event-client.ts +18 -10
  201. package/src/embed/hostEventClient/utils.spec.ts +176 -1
  202. package/src/embed/hostEventClient/utils.ts +51 -2
  203. package/src/embed/liveboard.spec.ts +4 -3
  204. package/src/embed/liveboard.ts +21 -15
  205. package/src/embed/spotter-utils.spec.ts +29 -0
  206. package/src/embed/spotter-utils.ts +16 -0
  207. package/src/embed/ts-embed.spec.ts +245 -30
  208. package/src/embed/ts-embed.ts +107 -74
  209. package/src/errors.ts +1 -1
  210. package/src/index.ts +10 -0
  211. package/src/react/all-types-export.ts +4 -0
  212. package/src/types.ts +44 -21
  213. package/src/utils/authService/tokenizedAuthService.spec.ts +2 -1
  214. package/src/utils/custom-actions.spec.ts +4 -2
  215. package/src/utils/filterConverter.spec.ts +801 -0
  216. package/src/utils/filterConverter.ts +304 -0
  217. package/src/utils/processData.spec.ts +2 -1
@@ -45,6 +45,7 @@ import {
45
45
  getHostEventsConfig,
46
46
  getValueFromWindow,
47
47
  deserializeParam,
48
+ getScrollableAncestors,
48
49
  } from '../utils';
49
50
  import { getCustomActions } from '../utils/custom-actions';
50
51
  import {
@@ -115,13 +116,17 @@ const TS_EMBED_ID = '_thoughtspot-embed';
115
116
  */
116
117
  const PRERENDER_CONTAINER_ORIGINAL_POSITION_KEY = 'tsEmbedOriginalPosition';
117
118
  const PRERENDER_WRAPPER_ID_PREFIX = 'tsEmbed-pre-render-wrapper-';
119
+ const PRERENDER_PARKED_TRANSFORM = 'translateY(-100%)';
120
+ /** The height createPreRenderWrapper() seeds before anything is measured. */
121
+ const UNMEASURED_WRAPPER_HEIGHT = '100vh';
118
122
 
119
123
  // The container applies UpdateEmbedParams through React state, so a delivered
120
124
  // post is not the same as the params being in effect.
121
125
  const UPDATE_EMBED_PARAMS_SETTLE_MS = 200;
122
126
 
123
- // The container ignores runtimeFilterParams/runtimeParameterParams unless truthy, so
124
- // null or '' leaves the previous embed's values in place. '&' is truthy and parses to {}.
127
+ // The container ignores runtimeFilterParams/runtimeParameterParams unless
128
+ // truthy, so null or '' leaves the previous embed's values in place. '&' is
129
+ // truthy and parses to {}.
125
130
  const NO_RUNTIME_PARAMS = '&';
126
131
 
127
132
  /**
@@ -229,8 +234,7 @@ export class TsEmbed {
229
234
 
230
235
  private preRenderContainerEl: HTMLElement = document.body;
231
236
 
232
- private containerScrollListener: (() => void) | null = null;
233
-
237
+ /** Last height the embedded app reported, once fullHeight has measured one. */
234
238
  protected hostEventClient: HostEventClient;
235
239
 
236
240
  protected isReadyForRenderPromise;
@@ -740,8 +744,9 @@ export class TsEmbed {
740
744
  ...this.viewConfig,
741
745
  ...queryParams,
742
746
  ...appInitData,
743
- // A show cycle has no URL to carry these, so the payload always states them
744
- // — including "none", which is the case that leaks the previous filters.
747
+ // A show cycle has no URL to carry these, so the payload always
748
+ // states them — including "none", which is the case that leaks the
749
+ // previous filters.
745
750
  runtimeFilterParams:
746
751
  getFilterQuery(this.viewConfig.runtimeFilters ?? []) || NO_RUNTIME_PARAMS,
747
752
  runtimeParameterParams:
@@ -1145,7 +1150,7 @@ export class TsEmbed {
1145
1150
  top: '0',
1146
1151
  left: '0',
1147
1152
  width: '100vw',
1148
- height: '100vh',
1153
+ height: UNMEASURED_WRAPPER_HEIGHT,
1149
1154
  };
1150
1155
  setStyleProperties(preRenderWrapper, initialPreRenderWrapperStyle);
1151
1156
 
@@ -1231,10 +1236,26 @@ export class TsEmbed {
1231
1236
  }
1232
1237
 
1233
1238
  /**
1234
- * Resolves the configured preRenderContainer to a live element, falling
1235
- * back to `document.body`. A string selector is re-queried on every call so
1236
- * a remounted container (E.g.: React replacing the node) resolves to the
1237
- * fresh element; an element passed directly cannot be re-resolved.
1239
+ * Resolves the configured preRenderContainer to a live element. A string
1240
+ * selector is re-queried on every call so a remounted container (E.g.: React
1241
+ * replacing the node) resolves to the fresh element; an element passed
1242
+ * directly cannot be re-resolved.
1243
+ *
1244
+ * With nothing configured the frame goes into the host element's nearest
1245
+ * scrolling ancestor, falling back to `document.body`.
1246
+ *
1247
+ * The wrapper is absolutely positioned, so it follows the page for free —
1248
+ * but only while it sits inside whatever scrolls. `document.body` is right
1249
+ * only when the document itself is the scroller; an app that scrolls an
1250
+ * inner element instead left the frame pinned to the viewport while the page
1251
+ * moved under it (SCAL-338563). Being inside the scroller also lets wheel
1252
+ * events chain out of the frame into it, which they cannot do from
1253
+ * `document.body`.
1254
+ *
1255
+ * A scrolling ancestor is layout chrome rather than route content, so it
1256
+ * outlives the embed in practice; and if it does go, the host element goes
1257
+ * with it and this instance is remounting anyway.
1258
+ * `reconcilePreRenderContainer` re-runs this on a detached container.
1238
1259
  */
1239
1260
  private resolvePreRenderContainerTarget(): HTMLElement {
1240
1261
  const containerConfig = this.getPreRenderConfig().containerSelector;
@@ -1251,7 +1272,13 @@ export class TsEmbed {
1251
1272
  } else if (containerConfig) {
1252
1273
  container = containerConfig;
1253
1274
  }
1254
- return (container as HTMLElement) ?? document.body;
1275
+ if (container) {
1276
+ return container as HTMLElement;
1277
+ }
1278
+ if (!this.hostElement) {
1279
+ return document.body;
1280
+ }
1281
+ return getScrollableAncestors(this.hostElement)[0] ?? document.body;
1255
1282
  }
1256
1283
 
1257
1284
  private inheritPreRenderContainer(): void {
@@ -1305,8 +1332,9 @@ export class TsEmbed {
1305
1332
  * Re-attaches the wrapper to a live container when the previously resolved
1306
1333
  * one has been detached or no longer holds the wrapper — e.g. the host app
1307
1334
  * remounted a custom preRenderContainer, which would otherwise leave a stale
1308
- * reference and collapse the wrapper. Only string selectors can be
1309
- * re-resolved; a container passed as an element is left untouched.
1335
+ * reference and collapse the wrapper. Selectors and the auto-resolved
1336
+ * scrolling ancestor are both re-resolved; a container passed as an element
1337
+ * is left untouched, as there is nothing to re-query.
1310
1338
  */
1311
1339
  private reconcilePreRenderContainer(): void {
1312
1340
  const wrapper = this.preRenderWrapper;
@@ -1325,14 +1353,6 @@ export class TsEmbed {
1325
1353
  if (resolved === stored && stored.contains(wrapper)) {
1326
1354
  return;
1327
1355
  }
1328
- if (this.containerScrollListener && stored !== resolved) {
1329
- if (stored !== document.body) {
1330
- stored.removeEventListener('scroll', this.containerScrollListener);
1331
- }
1332
- if (resolved !== document.body) {
1333
- resolved.addEventListener('scroll', this.containerScrollListener);
1334
- }
1335
- }
1336
1356
  this.preRenderContainerEl = resolved;
1337
1357
  this.applyPreRenderContainerPositioning();
1338
1358
  if (wrapper.parentNode !== resolved) {
@@ -1401,10 +1421,11 @@ export class TsEmbed {
1401
1421
  */
1402
1422
  protected setIFrameHeight(height: number | string): void {
1403
1423
  if (this.isPreRendered) {
1424
+ const next = getCssDimension(height);
1404
1425
  if (this.insertedDomEl) {
1405
- (this.insertedDomEl as HTMLElement).style.height = getCssDimension(height);
1426
+ (this.insertedDomEl as HTMLElement).style.height = next;
1406
1427
  } else if (this.preRenderWrapper) {
1407
- this.preRenderWrapper.style.height = getCssDimension(height);
1428
+ this.preRenderWrapper.style.height = next;
1408
1429
  }
1409
1430
  } else {
1410
1431
  // normal (non-preRender) mode: size the iframe directly
@@ -1615,8 +1636,9 @@ export class TsEmbed {
1615
1636
  (this.preRenderWrapper as any)[this.embedNodeKey] = this;
1616
1637
  }
1617
1638
 
1618
- // The flag lives on the wrapper because it describes the iframe: an instance
1619
- // hidden when the container announced itself would otherwise report false forever.
1639
+ // The flag lives on the wrapper because it describes the iframe: an
1640
+ // instance hidden when the container announced itself would otherwise
1641
+ // report false forever.
1620
1642
  private markEmbedContainerLoaded() {
1621
1643
  this.isEmbedContainerLoaded = true;
1622
1644
  if (this.preRenderWrapper) {
@@ -1672,7 +1694,8 @@ export class TsEmbed {
1672
1694
  const AUTH_INIT_FALLBACK_DELAY = 1000;
1673
1695
  // Wait for 1 second to ensure the embed container is loaded
1674
1696
  // This is a workaround to ensure the embed container is loaded
1675
- // this is needed until all clusters have EmbedListenerReady event
1697
+ // this is needed until all clusters have EmbedListenerReady
1698
+ // event
1676
1699
  setTimeout(processEmbedContainerReady, AUTH_INIT_FALLBACK_DELAY);
1677
1700
  } else if (source === EmbedEvent.EmbedListenerReady) {
1678
1701
  processEmbedContainerReady();
@@ -1967,15 +1990,27 @@ export class TsEmbed {
1967
1990
  }
1968
1991
 
1969
1992
  /**
1970
- * Detaches and clears the container scroll listener, if one is attached.
1971
- */
1972
- private removeContainerScrollListener(): void {
1973
- if (!this.containerScrollListener) {
1993
+ * Re-sizes the wrapper when its placeholder changes size.
1994
+ *
1995
+ * Position deliberately has no equivalent. An absolutely positioned wrapper
1996
+ * follows its containing block for free, so a correctly placed frame is the
1997
+ * browser's job and costs nothing; repositioning it on scroll would mean a
1998
+ * layout read and a style write per frame to compensate for placing it in
1999
+ * the wrong box. Size is the one thing the browser will not do for us: the
2000
+ * placeholder grows when the embedded app reports a new height, and the
2001
+ * wrapper has to be told.
2002
+ */
2003
+ private observePreRenderSize(): void {
2004
+ if (this.getPreRenderConfig().doNotTrackSize || typeof ResizeObserver === 'undefined') {
2005
+ return;
2006
+ }
2007
+ const placeholder = this.getPreRenderPlaceHolderElement();
2008
+ if (!placeholder) {
1974
2009
  return;
1975
2010
  }
1976
- const customContainer = this.getCustomPreRenderContainer();
1977
- customContainer?.removeEventListener('scroll', this.containerScrollListener);
1978
- this.containerScrollListener = null;
2011
+ this.resizeObserver?.disconnect();
2012
+ this.resizeObserver = new ResizeObserver(() => this.syncPreRenderStyle());
2013
+ this.resizeObserver.observe(placeholder);
1979
2014
  }
1980
2015
 
1981
2016
  /**
@@ -1985,7 +2020,7 @@ export class TsEmbed {
1985
2020
  public destroy(): void {
1986
2021
  try {
1987
2022
  this.removeFullscreenChangeHandler();
1988
- this.removeContainerScrollListener();
2023
+ this.resizeObserver?.disconnect();
1989
2024
  this.unsubscribeToEvents();
1990
2025
  this.preRenderWrapper?.remove();
1991
2026
  this.restorePreRenderContainerPosition();
@@ -2040,7 +2075,8 @@ export class TsEmbed {
2040
2075
  }
2041
2076
 
2042
2077
  // Subclasses that navigate the pre-render on show must await this before
2043
- // triggering Navigate. Resolves even on failure, so navigation is never blocked.
2078
+ // triggering Navigate. Resolves even on failure, so navigation is never
2079
+ // blocked.
2044
2080
  protected preRenderParamsApplied: Promise<void> = Promise.resolve();
2045
2081
 
2046
2082
  protected beforePrerenderVisible(): void {
@@ -2098,54 +2134,48 @@ export class TsEmbed {
2098
2134
 
2099
2135
  if (this.hostElement) {
2100
2136
  this.insertedDomEl = this.createPreRenderPlaceholder();
2101
- if ((this.viewConfig as { fullHeight: boolean }).fullHeight) {
2102
- // If fullHeight has already sized the wrapper, seed the placeholder
2103
- // with the same height so syncPreRenderStyle gets an accurate rect.
2104
- const existingHeight = this.preRenderWrapper.style.height;
2105
- if (existingHeight) {
2106
- (this.insertedDomEl as HTMLDivElement).style.height = existingHeight;
2107
- }
2137
+ // Carry a height fullHeight has already measured onto the fresh
2138
+ // placeholder, so a re-show does not flash at frameParams height.
2139
+ // UNMEASURED_WRAPPER_HEIGHT is what createPreRenderWrapper() seeds
2140
+ // before anything has been measured; treating that as a measurement
2141
+ // is what made a first reveal overshoot to a full viewport.
2142
+ const wrapperHeight = this.preRenderWrapper.style.height;
2143
+ if (
2144
+ (this.viewConfig as { fullHeight: boolean }).fullHeight
2145
+ && wrapperHeight
2146
+ && wrapperHeight !== UNMEASURED_WRAPPER_HEIGHT
2147
+ ) {
2148
+ (this.insertedDomEl as HTMLDivElement).style.height = wrapperHeight;
2108
2149
  }
2109
2150
 
2110
2151
  const placeHolderId = this.getPreRenderIds().placeHolder;
2111
2152
  // Remove any stale placeholder from a previous cycle. It is located
2112
- // via a subtree-wide querySelector, so it may be nested deeper than a
2113
- // direct child (E.g.: with fullHeight the host app can wrap it). Use
2114
- // Element.remove() — which detaches from whatever the real parent is —
2115
- // rather than hostElement.removeChild(), which throws NotFoundError
2116
- // when the match is not a direct child. Mirrors the wrapper/child
2117
- // cleanup in createPreRenderWrapper()/createPreRenderChild().
2153
+ // via a subtree-wide querySelector, so it may be nested deeper
2154
+ // than a direct child (E.g.: with fullHeight the host app can wrap
2155
+ // it). Use Element.remove() — which detaches from whatever the real
2156
+ // parent is — rather than hostElement.removeChild(), which throws
2157
+ // NotFoundError when the match is not a direct child. Mirrors the
2158
+ // wrapper/child cleanup in
2159
+ // createPreRenderWrapper()/createPreRenderChild().
2118
2160
  this.hostElement.querySelector(`#${placeHolderId}`)?.remove();
2119
2161
 
2120
2162
  this.hostElement.appendChild(this.insertedDomEl);
2121
2163
 
2122
2164
  this.syncPreRenderStyle();
2123
-
2124
- const customContainer = this.getCustomPreRenderContainer();
2125
- if (customContainer && !this.containerScrollListener) {
2126
- this.containerScrollListener = () => this.syncPreRenderStyle();
2127
- customContainer.addEventListener('scroll', this.containerScrollListener);
2128
- }
2129
-
2130
- if (!this.getPreRenderConfig().doNotTrackSize) {
2131
- const observeTarget = (this.insertedDomEl as HTMLElement) ?? this.hostElement;
2132
- this.resizeObserver = new ResizeObserver((entries) => {
2133
- entries.forEach((entry) => {
2134
- if (entry.target === observeTarget) {
2135
- this.syncPreRenderStyle();
2136
- }
2137
- });
2138
- });
2139
- this.resizeObserver.observe(observeTarget);
2140
- }
2165
+ this.observePreRenderSize();
2141
2166
  }
2142
2167
 
2143
2168
  removeStyleProperties(this.preRenderWrapper, [
2144
2169
  'z-index',
2145
2170
  'opacity',
2146
- 'pointer-events',
2147
2171
  'overflow',
2172
+ 'transform',
2148
2173
  ]);
2174
+ // Set rather than removed: a container that carries `pointer-events:
2175
+ // none` — the usual styling for a parking root that must not swallow
2176
+ // clicks — passes it down, and dropping the property here would leave
2177
+ // the inherited `none` in force and the frame dead to input.
2178
+ setStyleProperties(this.preRenderWrapper, { pointerEvents: 'auto' });
2149
2179
  this.subscribeToEvents();
2150
2180
 
2151
2181
  // Setup fullscreen change handler for prerendered components
@@ -2153,7 +2183,8 @@ export class TsEmbed {
2153
2183
  this.setupFullscreenChangeHandler();
2154
2184
  }
2155
2185
 
2156
- // Last, so everything above still sees the instance being taken over from.
2186
+ // Last, so everything above still sees the instance being taken over
2187
+ // from.
2157
2188
  this.takeOverPreRender();
2158
2189
 
2159
2190
  return this;
@@ -2224,14 +2255,16 @@ export class TsEmbed {
2224
2255
  top: '0',
2225
2256
  left: '0',
2226
2257
  overflow: 'hidden',
2258
+ // Keeps the measured size — so the next show needs no resize, which
2259
+ // is the point of pre-rendering — while lifting the box out of
2260
+ // the container's scrollable area. Overflow only extends
2261
+ // downwards, so a hidden frame parked above the top edge adds
2262
+ // none, where one left at full height shows as blank scroll space.
2263
+ transform: PRERENDER_PARKED_TRANSFORM,
2227
2264
  };
2228
2265
  setStyleProperties(this.preRenderWrapper, preRenderHideStyles);
2229
2266
 
2230
- this.removeContainerScrollListener();
2231
-
2232
- if (this.resizeObserver) {
2233
- this.resizeObserver.disconnect();
2234
- }
2267
+ this.resizeObserver?.disconnect();
2235
2268
 
2236
2269
  const placeHolderEle = this.getPreRenderPlaceHolderElement();
2237
2270
  if (placeHolderEle) {
package/src/errors.ts CHANGED
@@ -29,7 +29,7 @@ export const ERROR_MESSAGE = {
29
29
  SSR_ENVIRONMENT_ERROR: 'SSR environment detected. This function cannot be called in SSR environment.',
30
30
  UPDATE_PARAMS_FAILED: 'Failed to update embed parameters',
31
31
  INVALID_SPOTTER_DOCUMENTATION_URL: 'Invalid spotterDocumentationUrl. Please provide a valid http or https URL.',
32
- UPDATEFILTERS_INVALID_PAYLOAD: 'UpdateFilters requires a valid filter or filters array. Expected: { filter: { column, oper, values } } or { filters: [{ column, oper, values }, ...] }',
32
+ UPDATEFILTERS_INVALID_PAYLOAD: 'UpdateFilters requires a valid filter or filters array. Expected: { filter: { columnName, operator, values } } or { filters: [{ columnName, operator, values }, ...] }',
33
33
  DRILLDOWN_INVALID_PAYLOAD: 'DrillDown requires a valid points object. Expected: { points: { clickedPoint?, selectedPoints? }, autoDrillDown?, vizId? }',
34
34
  UPDATEPARAMETERS_INVALID_PAYLOAD: 'UpdateParameters received an invalid applicability. Expected: { level: LIVEBOARD | TAB | GROUP, targetId } where targetId is required for TAB and GROUP levels',
35
35
  };
package/src/index.ts CHANGED
@@ -151,6 +151,12 @@ import {
151
151
  NavigateRequest,
152
152
  SetActiveTabRequest,
153
153
  } from './contracts/host-event-contracts';
154
+ import {
155
+ convertFilterChangedToUpdateFiltersPayload,
156
+ FilterChangedPayload,
157
+ UpdateFiltersFilterParam,
158
+ UpdateFiltersPayload,
159
+ } from './utils/filterConverter';
154
160
 
155
161
  export {
156
162
  init,
@@ -318,6 +324,10 @@ export {
318
324
  DataLabelFilterOperator,
319
325
  TableTheme,
320
326
  TableContentDensity,
327
+ convertFilterChangedToUpdateFiltersPayload,
328
+ FilterChangedPayload,
329
+ UpdateFiltersFilterParam,
330
+ UpdateFiltersPayload,
321
331
  };
322
332
 
323
333
  export { resetCachedAuthToken } from './authToken';
@@ -128,4 +128,8 @@ export {
128
128
  StarterPromptCategory,
129
129
  StarterPreviewDataCategory,
130
130
  StarterPromptQuestion,
131
+ convertFilterChangedToUpdateFiltersPayload,
132
+ FilterChangedPayload,
133
+ UpdateFiltersFilterParam,
134
+ UpdateFiltersPayload,
131
135
  } from '../index';
package/src/types.ts CHANGED
@@ -5918,6 +5918,12 @@ export enum HostEvent {
5918
5918
  * ```js
5919
5919
  * const data = await liveboardEmbed.trigger(HostEvent.GetFilters);
5920
5920
  * console.log('data', data);
5921
+ * // {
5922
+ * // liveboardFilters: [ ... ],
5923
+ * // runtimeFilters: [
5924
+ * // { columnName: "Region", operator: "IN", values: ["West"] }
5925
+ * // ]
5926
+ * // }
5921
5927
  * ```
5922
5928
  * @example
5923
5929
  * ```js
@@ -5942,9 +5948,9 @@ export enum HostEvent {
5942
5948
  *
5943
5949
  * Each filter object must include the following attributes:
5944
5950
  *
5945
- * `column` - Name of the column to filter on.
5951
+ * `columnName` - Name of the column to filter on.
5946
5952
  *
5947
- * `oper` - Filter operator, for example, EQ, IN, CONTAINS.
5953
+ * `operator` - Filter operator, for example, EQ, IN, CONTAINS.
5948
5954
  * For information about the supported filter operators,
5949
5955
  * see link:https://developers.thoughtspot.com/docs/runtime-filters#rtOperator[Developer Documentation].
5950
5956
  *
@@ -5971,8 +5977,8 @@ export enum HostEvent {
5971
5977
  *
5972
5978
  * liveboardEmbed.trigger(HostEvent.UpdateFilters, {
5973
5979
  * filter: {
5974
- * column: "item type",
5975
- * oper: "IN",
5980
+ * columnName: "item type",
5981
+ * operator: "IN",
5976
5982
  * values: ["bags","shirts"]
5977
5983
  * }
5978
5984
  * });
@@ -5982,8 +5988,8 @@ export enum HostEvent {
5982
5988
  *
5983
5989
  * liveboardEmbed.trigger(HostEvent.UpdateFilters, {
5984
5990
  * filter: {
5985
- * column: "date",
5986
- * oper: "EQ",
5991
+ * columnName: "date",
5992
+ * operator: "EQ",
5987
5993
  * values: ["JULY","2023"],
5988
5994
  * type: "MONTH_YEAR"
5989
5995
  * }
@@ -5994,18 +6000,18 @@ export enum HostEvent {
5994
6000
  * ```js
5995
6001
  * liveboardEmbed.trigger(HostEvent.UpdateFilters, {
5996
6002
  * filters: [{
5997
- * column: "Item Type",
5998
- * oper: 'IN',
6003
+ * columnName: "Item Type",
6004
+ * operator: 'IN',
5999
6005
  * values: ["bags","shirts"]
6000
6006
  * },
6001
6007
  * {
6002
- * column: "Region",
6003
- * oper: 'IN',
6008
+ * columnName: "Region",
6009
+ * operator: 'IN',
6004
6010
  * values: ["West","Midwest"]
6005
6011
  * },
6006
6012
  * {
6007
- * column: "Date",
6008
- * oper: 'EQ',
6013
+ * columnName: "Date",
6014
+ * operator: 'EQ',
6009
6015
  * values: ["2023-07-31"],
6010
6016
  * type: "EXACT_DATE"
6011
6017
  * }]
@@ -6019,13 +6025,13 @@ export enum HostEvent {
6019
6025
  * ```js
6020
6026
  * liveboardEmbed.trigger(HostEvent.UpdateFilters, {
6021
6027
  * filters: [{
6022
- * column: "(Sample) Retail - Apparel::city",
6023
- * oper: 'IN',
6028
+ * columnName: "(Sample) Retail - Apparel::city",
6029
+ * operator: 'IN',
6024
6030
  * values: ["atlanta"]
6025
6031
  * },
6026
6032
  * {
6027
- * column: "(Sample) Retail - Apparel::Region",
6028
- * oper: 'IN',
6033
+ * columnName: "(Sample) Retail - Apparel::Region",
6034
+ * operator: 'IN',
6029
6035
  * values: ["West","Midwest"]
6030
6036
  * }]
6031
6037
  * });
@@ -6036,8 +6042,8 @@ export enum HostEvent {
6036
6042
  * import { ContextType } from '@thoughtspot/visual-embed-sdk';
6037
6043
  * liveboardEmbed.trigger(HostEvent.UpdateFilters, {
6038
6044
  * filter: {
6039
- * column: "item type",
6040
- * oper: "IN",
6045
+ * columnName: "item type",
6046
+ * operator: "IN",
6041
6047
  * values: ["shoes", "boots"]
6042
6048
  * }
6043
6049
  * }, ContextType.Liveboard);
@@ -6047,8 +6053,8 @@ export enum HostEvent {
6047
6053
  * // Scope the filter to a specific Liveboard tab
6048
6054
  * liveboardEmbed.trigger(HostEvent.UpdateFilters, {
6049
6055
  * filter: {
6050
- * column: "item type",
6051
- * oper: "IN",
6056
+ * columnName: "item type",
6057
+ * operator: "IN",
6052
6058
  * values: ["bags", "shirts"],
6053
6059
  * applicability: {
6054
6060
  * level: "TAB",
@@ -6057,6 +6063,12 @@ export enum HostEvent {
6057
6063
  * }
6058
6064
  * });
6059
6065
  * ```
6066
+ * Use `columnName` and `operator` - the same spelling as
6067
+ * {@link RuntimeFilter}. The older `column` and `oper` spellings are
6068
+ * deprecated; they are still accepted, but pass only one spelling of each
6069
+ * field, never both. To reapply the filter state captured from
6070
+ * {@link EmbedEvent.FilterChanged}, use `convertFilterChangedToUpdateFiltersPayload`
6071
+ * to convert its payload into the shape expected here.
6060
6072
  * @version SDK: 1.23.0 | ThoughtSpot: 9.4.0.cl
6061
6073
  */
6062
6074
  UpdateFilters = 'updateFilters',
@@ -7053,7 +7065,6 @@ export enum Param {
7053
7065
  IsStarterPromptsEnabled = 'enableStarterPrompts',
7054
7066
  UpdatedSpotterExperience = 'updatedSpotterExperience',
7055
7067
  SpotterDataSources = 'spotterDataSources',
7056
- AnalystId = 'analystId',
7057
7068
  OpenSpotterOnLiveboardByDefault = 'openSpotterOnLiveboardByDefault',
7058
7069
  ShowAnswerEditPanel = 'showAnswerEditPanel',
7059
7070
  }
@@ -9311,6 +9322,18 @@ export enum Action {
9311
9322
  * ```
9312
9323
  */
9313
9324
  DeleteCollection = 'deleteCollection',
9325
+ /**
9326
+ * Controls the visibility of the "Customize homepage" button on the
9327
+ * embedded home page.
9328
+ * @version SDK: 1.55.0 | ThoughtSpot Cloud: 26.12.0.cl
9329
+ * @example
9330
+ * ```js
9331
+ * {
9332
+ * hiddenActions: [Action.CustomizeHomepage],
9333
+ * }
9334
+ * ```
9335
+ */
9336
+ CustomizeHomepage = 'customizeHomepage',
9314
9337
  }
9315
9338
  export interface AnswerServiceType {
9316
9339
  getAnswer?: (offset: number, batchSize: number) => any;
@@ -79,7 +79,8 @@ describe('fetchPreauthInfoService', () => {
79
79
  });
80
80
  it('fetchPreauthInfoService if fetch fails', async () => {
81
81
  const mockFetch = jest.spyOn(tokenizedFetchModule, 'tokenizedFetch');
82
- // Prevent logger.error from reaching console.error (which throws in test env)
82
+ // Prevent logger.error from reaching console.error (which throws in
83
+ // test env)
83
84
  jest.spyOn(logger, 'error').mockImplementation(() => {});
84
85
 
85
86
  mockFetch.mockResolvedValueOnce({
@@ -439,7 +439,8 @@ describe('getCustomActions function', () => {
439
439
  metadataIds: { liveboardIds: ['lb-1'] },
440
440
  } as any;
441
441
  const result = getCustomActions([action]);
442
- // Expect an error containing 'none' for unsupported metadataIds on SPOTTER
442
+ // Expect an error containing 'none' for unsupported metadataIds on
443
+ // SPOTTER
443
444
  expect(result.errors.some((e) => e.includes('none'))).toBe(true);
444
445
  });
445
446
 
@@ -452,7 +453,8 @@ describe('getCustomActions function', () => {
452
453
  dataModelIds: { modelIds: ['model-1'] },
453
454
  } as any;
454
455
  const result = getCustomActions([action]);
455
- // Expect an error containing 'none' for unsupported dataModelIds on LIVEBOARD
456
+ // Expect an error containing 'none' for unsupported dataModelIds
457
+ // on LIVEBOARD
456
458
  expect(result.errors.some((e) => e.includes('none'))).toBe(true);
457
459
  });
458
460
  });