@thoughtspot/visual-embed-sdk 1.52.2-beta → 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 (71) hide show
  1. package/cjs/package.json +2 -2
  2. package/cjs/src/embed/app.js +1 -1
  3. package/cjs/src/embed/app.js.map +1 -1
  4. package/cjs/src/embed/auto-frame-renderer.js +1 -1
  5. package/cjs/src/embed/auto-frame-renderer.js.map +1 -1
  6. package/cjs/src/embed/bodyless-conversation.js +1 -1
  7. package/cjs/src/embed/bodyless-conversation.js.map +1 -1
  8. package/cjs/src/embed/conversation.js +1 -1
  9. package/cjs/src/embed/conversation.js.map +1 -1
  10. package/cjs/src/embed/liveboard.js +1 -1
  11. package/cjs/src/embed/liveboard.js.map +1 -1
  12. package/cjs/src/embed/search-bar.js +1 -1
  13. package/cjs/src/embed/search-bar.js.map +1 -1
  14. package/cjs/src/embed/search.js +1 -1
  15. package/cjs/src/embed/search.js.map +1 -1
  16. package/cjs/src/embed/search.spec.js +0 -20
  17. package/cjs/src/embed/search.spec.js.map +1 -1
  18. package/cjs/src/embed/ts-embed.d.ts +34 -12
  19. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  20. package/cjs/src/embed/ts-embed.js +98 -142
  21. package/cjs/src/embed/ts-embed.js.map +1 -1
  22. package/cjs/src/embed/ts-embed.spec.d.ts.map +1 -1
  23. package/cjs/src/embed/ts-embed.spec.js +171 -138
  24. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  25. package/dist/{index-CcBYlGo9.js → index-BrJMbt8r.js} +1 -1
  26. package/dist/src/embed/ts-embed.d.ts +34 -12
  27. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  28. package/dist/src/embed/ts-embed.spec.d.ts.map +1 -1
  29. package/dist/tsembed-react.es.js +108 -152
  30. package/dist/tsembed-react.js +107 -151
  31. package/dist/tsembed.es.js +109 -153
  32. package/dist/tsembed.js +108 -152
  33. package/dist/visual-embed-sdk-react-full.d.ts +34 -12
  34. package/dist/visual-embed-sdk-react.d.ts +34 -12
  35. package/dist/visual-embed-sdk.d.ts +34 -12
  36. package/lib/package.json +2 -2
  37. package/lib/src/embed/app.js +1 -1
  38. package/lib/src/embed/app.js.map +1 -1
  39. package/lib/src/embed/auto-frame-renderer.js +1 -1
  40. package/lib/src/embed/auto-frame-renderer.js.map +1 -1
  41. package/lib/src/embed/bodyless-conversation.js +1 -1
  42. package/lib/src/embed/bodyless-conversation.js.map +1 -1
  43. package/lib/src/embed/conversation.js +1 -1
  44. package/lib/src/embed/conversation.js.map +1 -1
  45. package/lib/src/embed/liveboard.js +1 -1
  46. package/lib/src/embed/liveboard.js.map +1 -1
  47. package/lib/src/embed/search-bar.js +1 -1
  48. package/lib/src/embed/search-bar.js.map +1 -1
  49. package/lib/src/embed/search.js +1 -1
  50. package/lib/src/embed/search.js.map +1 -1
  51. package/lib/src/embed/search.spec.js +0 -20
  52. package/lib/src/embed/search.spec.js.map +1 -1
  53. package/lib/src/embed/ts-embed.d.ts +34 -12
  54. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  55. package/lib/src/embed/ts-embed.js +99 -143
  56. package/lib/src/embed/ts-embed.js.map +1 -1
  57. package/lib/src/embed/ts-embed.spec.d.ts.map +1 -1
  58. package/lib/src/embed/ts-embed.spec.js +172 -139
  59. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  60. package/lib/src/visual-embed-sdk.d.ts +34 -12
  61. package/package.json +2 -2
  62. package/src/embed/app.ts +1 -1
  63. package/src/embed/auto-frame-renderer.ts +1 -1
  64. package/src/embed/bodyless-conversation.ts +1 -1
  65. package/src/embed/conversation.ts +1 -1
  66. package/src/embed/liveboard.ts +1 -1
  67. package/src/embed/search-bar.tsx +1 -1
  68. package/src/embed/search.spec.ts +0 -23
  69. package/src/embed/search.ts +1 -1
  70. package/src/embed/ts-embed.spec.ts +213 -179
  71. package/src/embed/ts-embed.ts +96 -149
@@ -12335,7 +12335,7 @@ class TsEmbed {
12335
12335
  private defaultHiddenActions;
12336
12336
  private resizeObserver;
12337
12337
  private preRenderContainerEl;
12338
- private containerScrollListener;
12338
+ /** Last height the embedded app reported, once fullHeight has measured one. */
12339
12339
  protected hostEventClient: HostEventClient;
12340
12340
  protected isReadyForRenderPromise: Promise<void>;
12341
12341
  protected shouldWaitForRenderPromise: boolean;
@@ -12550,7 +12550,6 @@ class TsEmbed {
12550
12550
  protected getV1EmbedBasePath(queryString: string): string;
12551
12551
  protected getEmbedParams(): string;
12552
12552
  protected getEmbedParamsObject(): Record<any, any>;
12553
- protected getUrlQueryParamsObject(): Record<any, any>;
12554
12553
  protected getRootIframeSrc(): string;
12555
12554
  protected createIframeEl(frameSrc: string): HTMLIFrameElement;
12556
12555
  /**
@@ -12592,10 +12591,26 @@ class TsEmbed {
12592
12591
  */
12593
12592
  private createPreRenderPlaceholder;
12594
12593
  /**
12595
- * Resolves the configured preRenderContainer to a live element, falling
12596
- * back to `document.body`. A string selector is re-queried on every call so
12597
- * a remounted container (E.g.: React replacing the node) resolves to the
12598
- * fresh element; an element passed directly cannot be re-resolved.
12594
+ * Resolves the configured preRenderContainer to a live element. A string
12595
+ * selector is re-queried on every call so a remounted container (E.g.: React
12596
+ * replacing the node) resolves to the fresh element; an element passed
12597
+ * directly cannot be re-resolved.
12598
+ *
12599
+ * With nothing configured the frame goes into the host element's nearest
12600
+ * scrolling ancestor, falling back to `document.body`.
12601
+ *
12602
+ * The wrapper is absolutely positioned, so it follows the page for free —
12603
+ * but only while it sits inside whatever scrolls. `document.body` is right
12604
+ * only when the document itself is the scroller; an app that scrolls an
12605
+ * inner element instead left the frame pinned to the viewport while the page
12606
+ * moved under it (SCAL-338563). Being inside the scroller also lets wheel
12607
+ * events chain out of the frame into it, which they cannot do from
12608
+ * `document.body`.
12609
+ *
12610
+ * A scrolling ancestor is layout chrome rather than route content, so it
12611
+ * outlives the embed in practice; and if it does go, the host element goes
12612
+ * with it and this instance is remounting anyway.
12613
+ * `reconcilePreRenderContainer` re-runs this on a detached container.
12599
12614
  */
12600
12615
  private resolvePreRenderContainerTarget;
12601
12616
  private inheritPreRenderContainer;
@@ -12612,8 +12627,9 @@ class TsEmbed {
12612
12627
  * Re-attaches the wrapper to a live container when the previously resolved
12613
12628
  * one has been detached or no longer holds the wrapper — e.g. the host app
12614
12629
  * remounted a custom preRenderContainer, which would otherwise leave a stale
12615
- * reference and collapse the wrapper. Only string selectors can be
12616
- * re-resolved; a container passed as an element is left untouched.
12630
+ * reference and collapse the wrapper. Selectors and the auto-resolved
12631
+ * scrolling ancestor are both re-resolved; a container passed as an element
12632
+ * is left untouched, as there is nothing to re-query.
12617
12633
  */
12618
12634
  private reconcilePreRenderContainer;
12619
12635
  protected insertIntoDOMForPreRender(child: string | Node): void;
@@ -12829,9 +12845,17 @@ class TsEmbed {
12829
12845
  */
12830
12846
  private restorePreRenderContainerPosition;
12831
12847
  /**
12832
- * Detaches and clears the container scroll listener, if one is attached.
12848
+ * Re-sizes the wrapper when its placeholder changes size.
12849
+ *
12850
+ * Position deliberately has no equivalent. An absolutely positioned wrapper
12851
+ * follows its containing block for free, so a correctly placed frame is the
12852
+ * browser's job and costs nothing; repositioning it on scroll would mean a
12853
+ * layout read and a style write per frame to compensate for placing it in
12854
+ * the wrong box. Size is the one thing the browser will not do for us: the
12855
+ * placeholder grows when the embedded app reports a new height, and the
12856
+ * wrapper has to be told.
12833
12857
  */
12834
- private removeContainerScrollListener;
12858
+ private observePreRenderSize;
12835
12859
  /**
12836
12860
  * Destroys the ThoughtSpot embed, and remove any nodes from the DOM.
12837
12861
  * @version SDK: 1.19.1 | ThoughtSpot: *
@@ -12847,8 +12871,6 @@ class TsEmbed {
12847
12871
  */
12848
12872
  prerenderGeneric(): Promise<any>;
12849
12873
  protected preRenderParamsApplied: Promise<void>;
12850
- protected isConfigExcludedFromUrl(): boolean;
12851
- protected triggerUpdateEmbedParams(): Promise<void>;
12852
12874
  protected beforePrerenderVisible(): void;
12853
12875
  /**
12854
12876
  * Displays the pre-rendered component inside the host element.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thoughtspot/visual-embed-sdk",
3
- "version": "1.52.2-beta",
3
+ "version": "1.52.2-beta.0",
4
4
  "description": "ThoughtSpot Embed SDK",
5
5
  "module": "lib/src/index.js",
6
6
  "main": "dist/tsembed.js",
@@ -65,7 +65,7 @@
65
65
  "test": "npm run test-sdk",
66
66
  "posttest": "cat ./coverage/sdk/lcov.info | npx coveralls-next || echo 'Warning: Coveralls upload failed but not breaking the build.'",
67
67
  "is-publish-allowed": "node scripts/is-publish-allowed.js",
68
- "prepublishOnly": "npm run is-publish-allowed && npm run tsc && npm run bundle-dts-file && npm run bundle-dts && npm run bundle-dts-react && npm run bundle-dts-react-full && npm run build",
68
+ "prepublishOnly": "npm run is-publish-allowed && npm run test && npm run tsc && npm run bundle-dts-file && npm run bundle-dts && npm run bundle-dts-react && npm run bundle-dts-react-full && npm run build",
69
69
  "check-size": "npm run build && size-limit",
70
70
  "publish-dev": "npm publish --tag dev",
71
71
  "publish-prod": "npm publish --tag latest",
package/src/embed/app.ts CHANGED
@@ -991,7 +991,7 @@ export class AppEmbed extends V1Embed {
991
991
  * embedded Liveboard or visualization.
992
992
  */
993
993
  protected getEmbedParams() {
994
- const params = this.getUrlQueryParamsObject();
994
+ const params = this.getEmbedParamsObject();
995
995
  return getQueryParamString(params, true);
996
996
  }
997
997
 
@@ -108,7 +108,7 @@ class AutoFrameRenderer extends TsEmbed {
108
108
  * @returns The constructed URL to use for the ThoughtSpot embed iframe.
109
109
  */
110
110
  private getMCPIframeSrc(sourceSrc: string) {
111
- const queryParams = this.getUrlQueryParamsObject();
111
+ const queryParams = this.getEmbedParamsObject();
112
112
  const sourceURL = new URL(sourceSrc);
113
113
  const existingQueryParams = sourceURL.searchParams;
114
114
  const existingQueryParamsObject = Object.fromEntries(existingQueryParams);
@@ -56,7 +56,7 @@ export class ConversationMessage extends TsEmbed {
56
56
  messageId,
57
57
  } = this.viewConfig;
58
58
  const path = 'conv-assist-answer';
59
- const queryParams = this.getUrlQueryParamsObject();
59
+ const queryParams = this.getEmbedParamsObject();
60
60
 
61
61
  let query = '';
62
62
  const queryParamsString = getQueryParamString(queryParams, true);
@@ -931,7 +931,7 @@ export class SpotterEmbed extends TsEmbed {
931
931
  const path = sharedConversationId
932
932
  ? `insights/conv-assist/s/${encodeURIComponent(sharedConversationId)}`
933
933
  : 'insights/conv-assist';
934
- const queryParams = this.getUrlQueryParamsObject();
934
+ const queryParams = this.getEmbedParamsObject();
935
935
 
936
936
  let query = '';
937
937
  const queryParamsString = getQueryParamString(queryParams, true);
@@ -599,7 +599,7 @@ export class LiveboardEmbed extends V1Embed {
599
599
  * embedded Liveboard or visualization.
600
600
  */
601
601
  protected getEmbedParams() {
602
- const params = this.getUrlQueryParamsObject();
602
+ const params = this.getEmbedParamsObject();
603
603
  const queryParams = getQueryParamString(params, true);
604
604
  return queryParams;
605
605
  }
@@ -163,7 +163,7 @@ export class SearchBarEmbed extends TsEmbed {
163
163
  * @param dataSources A list of data source GUIDs
164
164
  */
165
165
  private getIFrameSrc() {
166
- const queryParams = this.getUrlQueryParamsObject();
166
+ const queryParams = this.getEmbedParamsObject();
167
167
  const path = 'search-bar-embed';
168
168
 
169
169
  let query = '';
@@ -854,29 +854,6 @@ test('should pass forceTable parameter when forceTable is true', async () => {
854
854
  });
855
855
 
856
856
  describe('SearchBarEmbed tests', () => {
857
- test('should keep view config off the URL when excludeConfigFromURL is set', async () => {
858
- const searchBarEmbed = new SearchBarEmbed(getRootEl() as any, {
859
- ...defaultViewConfig,
860
- dataSources: ['source-1', 'source-2'],
861
- additionalFlags: { excludeConfigFromURL: true },
862
- } as any);
863
- searchBarEmbed.render();
864
- await executeAfterWait(() => {
865
- const iframeSrc = getIFrameSrc();
866
-
867
- // Bootstrap params stay: the handshake, the embed marker that
868
- // picks the app shell, and the data sources it boots against.
869
- expect(iframeSrc).toContain('hostAppUrl=');
870
- expect(iframeSrc).toContain('isSearchEmbed=true');
871
- expect(iframeSrc).toContain('dataSources');
872
-
873
- // The rest of the view config goes over
874
- // HostEvent.UpdateEmbedParams.
875
- expect(iframeSrc).not.toContain('hideAction');
876
- expect(iframeSrc).not.toContain('useLastSelectedDataSource');
877
- });
878
- });
879
-
880
857
  test('should pass dataSources parameter when dataSources array is provided', async () => {
881
858
  const searchBarEmbed = new SearchBarEmbed(getRootEl() as any, {
882
859
  ...defaultViewConfig,
@@ -546,7 +546,7 @@ export class SearchEmbed extends TsEmbed {
546
546
  excludeRuntimeParametersfromURL,
547
547
  excludeRuntimeFiltersfromURL,
548
548
  } = this.viewConfig;
549
- const queryParams = this.getUrlQueryParamsObject();
549
+ const queryParams = this.getEmbedParamsObject();
550
550
  let query = '';
551
551
  const queryParamsString = getQueryParamString(queryParams, true);
552
552
  if (queryParamsString) {
@@ -71,6 +71,10 @@ import * as processData from '../utils/processData';
71
71
  jest.mock('../utils/processTrigger');
72
72
 
73
73
  const mockProcessTrigger = processTrigger as jest.Mock;
74
+
75
+ const flushAnimationFrame = () => new Promise<void>((resolve) => {
76
+ requestAnimationFrame(() => resolve());
77
+ });
74
78
  const mockHandleInterceptEvent = jest.spyOn(apiIntercept, 'handleInterceptEvent');
75
79
  const defaultViewConfig = {
76
80
  frameParams: {
@@ -2620,12 +2624,14 @@ describe('Unit test case for ts embed', () => {
2620
2624
  contentRect: { height: 297, width: 987 },
2621
2625
  },
2622
2626
  ]);
2627
+ // Syncs are coalesced to one per frame.
2628
+ await flushAnimationFrame();
2623
2629
 
2624
2630
  expect(preRenderWrapper.style.height).toEqual(`${297}px`);
2625
2631
  expect(preRenderWrapper.style.width).toEqual(`${987}px`);
2626
2632
 
2627
2633
  expect(preRenderWrapper.style.opacity).toBe('');
2628
- expect(preRenderWrapper.style.pointerEvents).toBe('');
2634
+ expect(preRenderWrapper.style.pointerEvents).toBe('auto');
2629
2635
  expect(preRenderWrapper.style.zIndex).toBe('');
2630
2636
 
2631
2637
  libEmbed.hidePreRender();
@@ -2662,7 +2668,7 @@ describe('Unit test case for ts embed', () => {
2662
2668
  const preRenderWrapper = document.getElementById(preRenderIds.wrapper);
2663
2669
 
2664
2670
  expect(preRenderWrapper.style.opacity).toBe('');
2665
- expect(preRenderWrapper.style.pointerEvents).toBe('');
2671
+ expect(preRenderWrapper.style.pointerEvents).toBe('auto');
2666
2672
  expect(preRenderWrapper.style.zIndex).toBe('');
2667
2673
  });
2668
2674
 
@@ -2835,6 +2841,211 @@ describe('Unit test case for ts embed', () => {
2835
2841
  (logger.debug as any).mockRestore();
2836
2842
  });
2837
2843
 
2844
+ describe('position tracking (SCAL-338563)', () => {
2845
+ /**
2846
+ * A host app whose page scrolls in an inner element rather than the
2847
+ * document — the shape both reported cases have. The placeholder's
2848
+ * rect is driven off that element's scrollTop, the way a real
2849
+ * layout would move it.
2850
+ */
2851
+ const mountInNestedScroller = () => {
2852
+ createRootEleForEmbed();
2853
+ const scroller = document.createElement('div');
2854
+ scroller.id = 'inner-scroller';
2855
+ document.body.appendChild(scroller);
2856
+ scroller.appendChild(document.getElementById('tsEmbedDiv'));
2857
+
2858
+ let scrollTop = 0;
2859
+ Object.defineProperty(scroller, 'scrollTop', {
2860
+ configurable: true,
2861
+ get: () => scrollTop,
2862
+ set: (value) => {
2863
+ scrollTop = value;
2864
+ },
2865
+ });
2866
+ scroller.getBoundingClientRect = () =>
2867
+ ({
2868
+ x: 0, y: 0, width: 800, height: 600,
2869
+ top: 0, left: 0, bottom: 600, right: 800,
2870
+ } as DOMRect);
2871
+
2872
+ const placeholderTopAtRest = 400;
2873
+ const readPlaceholderTop = () => placeholderTopAtRest - scroller.scrollTop;
2874
+
2875
+ return { scroller, readPlaceholderTop };
2876
+ };
2877
+
2878
+ const stubPlaceholderRect = (embed: any, readTop: () => number) => {
2879
+ const placeholder = embed.getPreRenderPlaceHolderElement() as HTMLElement;
2880
+ placeholder.getBoundingClientRect = () => {
2881
+ const top = readTop();
2882
+ return {
2883
+ x: 0, y: top, width: 800, height: 300,
2884
+ top, left: 0, bottom: top + 300, right: 800,
2885
+ } as DOMRect;
2886
+ };
2887
+ return placeholder;
2888
+ };
2889
+
2890
+ it('should stop tracking once the frame is hidden', async () => {
2891
+ const { scroller, readPlaceholderTop } = mountInNestedScroller();
2892
+
2893
+ const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
2894
+ preRenderId: 'scroll-stops-on-hide',
2895
+ liveboardId: 'myLiveboardId',
2896
+ });
2897
+ libEmbed.preRender();
2898
+ await waitFor(() => !!getIFrameEl());
2899
+ await libEmbed.showPreRender();
2900
+
2901
+ stubPlaceholderRect(libEmbed, readPlaceholderTop);
2902
+ libEmbed.hidePreRender();
2903
+
2904
+ const syncSpy = jest.spyOn(libEmbed, 'syncPreRenderStyle');
2905
+ scroller.scrollTop = 250;
2906
+ scroller.dispatchEvent(new Event('scroll'));
2907
+ await flushAnimationFrame();
2908
+
2909
+ expect(syncSpy).not.toHaveBeenCalled();
2910
+
2911
+ syncSpy.mockRestore();
2912
+ libEmbed.destroy();
2913
+ scroller.remove();
2914
+ });
2915
+
2916
+ it('should default the container to the host scrolling ancestor', async () => {
2917
+ const { scroller } = mountInNestedScroller();
2918
+ scroller.style.overflowY = 'auto';
2919
+
2920
+ const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
2921
+ preRenderId: 'auto-scroll-container',
2922
+ liveboardId: 'myLiveboardId',
2923
+ });
2924
+ libEmbed.preRender();
2925
+ await waitFor(() => !!getIFrameEl());
2926
+ await libEmbed.showPreRender();
2927
+
2928
+ const wrapper = document.getElementById(libEmbed.getPreRenderIds().wrapper);
2929
+ // In the scroller, so the browser moves it — no repositioning.
2930
+ expect(wrapper.parentElement).toBe(scroller);
2931
+ expect((libEmbed as any).preRenderContainerEl).toBe(scroller);
2932
+
2933
+ libEmbed.destroy();
2934
+ scroller.remove();
2935
+ });
2936
+
2937
+ it('should still use document.body when nothing scrolls', async () => {
2938
+ createRootEleForEmbed();
2939
+
2940
+ const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
2941
+ preRenderId: 'no-scroller-body',
2942
+ liveboardId: 'myLiveboardId',
2943
+ });
2944
+ libEmbed.preRender();
2945
+ await waitFor(() => !!getIFrameEl());
2946
+ await libEmbed.showPreRender();
2947
+
2948
+ const wrapper = document.getElementById(libEmbed.getPreRenderIds().wrapper);
2949
+ expect(wrapper.parentElement).toBe(document.body);
2950
+
2951
+ libEmbed.destroy();
2952
+ });
2953
+
2954
+ it('should set pointer-events on show rather than dropping the property', async () => {
2955
+ createRootEleForEmbed();
2956
+ const container = document.createElement('div');
2957
+ container.id = 'parking-root';
2958
+ // Usual styling for a parking root: no swallowed clicks.
2959
+ container.style.pointerEvents = 'none';
2960
+ document.body.appendChild(container);
2961
+
2962
+ const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
2963
+ preRenderId: 'pointer-events-restored',
2964
+ liveboardId: 'myLiveboardId',
2965
+ preRenderContainer: '#parking-root',
2966
+ });
2967
+ libEmbed.preRender();
2968
+ await waitFor(() => !!getIFrameEl());
2969
+ await libEmbed.showPreRender();
2970
+
2971
+ const wrapper = document.getElementById(libEmbed.getPreRenderIds().wrapper);
2972
+ // Merely removing the property would leave the container's
2973
+ // inherited `none` in force, and the frame dead to input.
2974
+ expect(wrapper.style.pointerEvents).toBe('auto');
2975
+
2976
+ libEmbed.destroy();
2977
+ container.remove();
2978
+ });
2979
+
2980
+ it('should park a hidden wrapper out of the scrollable area', async () => {
2981
+ createRootEleForEmbed();
2982
+
2983
+ const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
2984
+ preRenderId: 'hidden-parked',
2985
+ liveboardId: 'myLiveboardId',
2986
+ });
2987
+ libEmbed.preRender();
2988
+ await waitFor(() => !!getIFrameEl());
2989
+ await libEmbed.showPreRender();
2990
+
2991
+ const wrapper = document.getElementById(libEmbed.getPreRenderIds().wrapper);
2992
+ expect(wrapper.style.transform).toBe('');
2993
+
2994
+ libEmbed.hidePreRender();
2995
+ expect(wrapper.style.transform).toBe('translateY(-100%)');
2996
+
2997
+ await libEmbed.showPreRender();
2998
+ expect(wrapper.style.transform).toBe('');
2999
+
3000
+ libEmbed.destroy();
3001
+ });
3002
+
3003
+ it('should not seed the placeholder with the unmeasured wrapper height', async () => {
3004
+ createRootEleForEmbed();
3005
+
3006
+ const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
3007
+ preRenderId: 'unmeasured-height',
3008
+ liveboardId: 'myLiveboardId',
3009
+ fullHeight: true,
3010
+ frameParams: { height: 420 },
3011
+ });
3012
+ libEmbed.preRender();
3013
+ await waitFor(() => !!getIFrameEl());
3014
+
3015
+ const wrapper = document.getElementById(libEmbed.getPreRenderIds().wrapper);
3016
+ // createPreRenderWrapper() seeds 100vh, not a measurement.
3017
+ expect(wrapper.style.height).toBe('100vh');
3018
+
3019
+ await libEmbed.showPreRender();
3020
+
3021
+ const placeholder = (libEmbed as any).getPreRenderPlaceHolderElement() as HTMLElement;
3022
+ expect(placeholder.style.height).toBe('420px');
3023
+
3024
+ libEmbed.destroy();
3025
+ });
3026
+
3027
+ it('should seed the placeholder with a height fullHeight has measured', async () => {
3028
+ createRootEleForEmbed();
3029
+
3030
+ const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
3031
+ preRenderId: 'measured-height',
3032
+ liveboardId: 'myLiveboardId',
3033
+ fullHeight: true,
3034
+ frameParams: { height: 420 },
3035
+ });
3036
+ libEmbed.preRender();
3037
+ await waitFor(() => !!getIFrameEl());
3038
+
3039
+ (libEmbed as any).setIFrameHeight(1180);
3040
+ await libEmbed.showPreRender();
3041
+
3042
+ const placeholder = (libEmbed as any).getPreRenderPlaceHolderElement() as HTMLElement;
3043
+ expect(placeholder.style.height).toBe('1180px');
3044
+
3045
+ libEmbed.destroy();
3046
+ });
3047
+ });
3048
+
2838
3049
  describe('preRenderContainer', () => {
2839
3050
  it('should append preRenderWrapper to document.body when preRenderContainer is not set', async () => {
2840
3051
  createRootEleForEmbed();
@@ -3108,21 +3319,16 @@ describe('Unit test case for ts embed', () => {
3108
3319
 
3109
3320
  // Simulate React remounting the container: the old node (with
3110
3321
  // our wrapper) is detached and a fresh node takes its place.
3111
- const removeSpy = jest.spyOn(oldContainer, 'removeEventListener');
3112
3322
  oldContainer.remove();
3113
3323
  const newContainer = document.createElement('div');
3114
3324
  newContainer.id = 'remount-container';
3115
3325
  document.body.appendChild(newContainer);
3116
- const addSpy = jest.spyOn(newContainer, 'addEventListener');
3117
3326
 
3118
3327
  // A reposition (scroll/resize/re-show) heals the stale ref.
3119
3328
  libEmbed.syncPreRenderStyle();
3120
3329
 
3121
3330
  expect(newContainer.contains(wrapper)).toBe(true);
3122
3331
  expect(oldContainer.contains(wrapper)).toBe(false);
3123
- // The scroll listener is migrated to the live container.
3124
- expect(removeSpy).toHaveBeenCalledWith('scroll', expect.any(Function));
3125
- expect(addSpy).toHaveBeenCalledWith('scroll', expect.any(Function));
3126
3332
 
3127
3333
  libEmbed.destroy();
3128
3334
  newContainer.remove();
@@ -6695,175 +6901,3 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
6695
6901
  });
6696
6902
  });
6697
6903
  });
6698
-
6699
- describe('excludeConfigFromURL', () => {
6700
- const lbConfig = {
6701
- liveboardId,
6702
- hiddenActions: [Action.Download],
6703
- additionalFlags: { internalBlinkFlag: true },
6704
- liveboardV2: true,
6705
- };
6706
-
6707
- const withPostMessage = (viewConfig: any) => ({
6708
- ...viewConfig,
6709
- additionalFlags: { ...viewConfig.additionalFlags, excludeConfigFromURL: true },
6710
- });
6711
-
6712
- const renderAndGetSrc = async (viewConfig: any) => {
6713
- const embed = new LiveboardEmbed(getRootEl(), { ...defaultViewConfig, ...viewConfig });
6714
- await embed.render();
6715
- await waitFor(() => !!getIFrameEl());
6716
- return { embed, src: getIFrameSrc() };
6717
- };
6718
-
6719
- const signalFrameReady = () => {
6720
- postMessageToParent(getIFrameEl().contentWindow, {
6721
- type: EmbedEvent.EmbedListenerReady,
6722
- });
6723
- };
6724
-
6725
- beforeEach(() => {
6726
- document.body.innerHTML = getDocumentBody();
6727
- mockProcessTrigger.mockReset();
6728
- mockProcessTrigger.mockResolvedValue({});
6729
- init({
6730
- thoughtSpotHost,
6731
- authType: AuthType.None,
6732
- });
6733
- });
6734
-
6735
- test('leaves the URL untouched when the flag is not set', async () => {
6736
- const { src } = await renderAndGetSrc(lbConfig);
6737
-
6738
- expect(src).toContain('hideAction=');
6739
- expect(src).toContain('internalBlinkFlag=true');
6740
- expect(src).toContain('isPinboardV2Enabled=true');
6741
- });
6742
-
6743
- test('keeps only the bootstrap params on the URL when the flag is set', async () => {
6744
- const { src } = await renderAndGetSrc(withPostMessage(lbConfig));
6745
-
6746
- // Boot and auth handshake stays on the URL.
6747
- expect(src).toContain('hostAppUrl=');
6748
- expect(src).toContain(`authType=${AuthType.None}`);
6749
- expect(src).toContain(`sdkVersion=${version}`);
6750
- expect(src).toContain('blockNonEmbedFullAppAccess=true');
6751
- expect(src).toContain('viewPortHeight=');
6752
-
6753
- // View configuration and internal flags do not.
6754
- expect(src).not.toContain('hideAction=');
6755
- expect(src).not.toContain('internalBlinkFlag');
6756
- expect(src).not.toContain('isPinboardV2Enabled');
6757
- expect(src).not.toContain('enableDataPanelV2');
6758
- });
6759
-
6760
- test('strips a non-bootstrap additionalFlag from the URL when the flag is set', async () => {
6761
- const { src } = await renderAndGetSrc({
6762
- ...lbConfig,
6763
- additionalFlags: {
6764
- internalBlinkFlag: true,
6765
- someCustomFlag: 'abc',
6766
- excludeConfigFromURL: true,
6767
- },
6768
- });
6769
-
6770
- expect(src).not.toContain('internalBlinkFlag');
6771
- expect(src).not.toContain('someCustomFlag');
6772
- });
6773
-
6774
- test('keeps an additionalFlag that overrides a bootstrap param on the URL', async () => {
6775
- const { src } = await renderAndGetSrc({
6776
- ...lbConfig,
6777
- additionalFlags: { [Param.OverrideOrgId]: 42, excludeConfigFromURL: true },
6778
- });
6779
-
6780
- expect(src).toContain(`${Param.OverrideOrgId}=42`);
6781
- });
6782
-
6783
- test('keeps the deep-link route on the URL when the flag is set', async () => {
6784
- const { src } = await renderAndGetSrc(withPostMessage(lbConfig));
6785
-
6786
- expect(src).toContain(`/embed/viz/${liveboardId}`);
6787
- });
6788
-
6789
- test('sends the full config over UpdateEmbedParams once the frame is ready', async () => {
6790
- await renderAndGetSrc(withPostMessage(lbConfig));
6791
-
6792
- signalFrameReady();
6793
-
6794
- await executeAfterWait(() => {
6795
- expect(mockProcessTrigger).toHaveBeenCalledWith(
6796
- expect.any(Object),
6797
- HostEvent.UpdateEmbedParams,
6798
- expect.any(String),
6799
- expect.objectContaining({
6800
- liveboardId,
6801
- hiddenActions: [Action.Download],
6802
- internalBlinkFlag: true,
6803
- [Param.HideActions]: expect.arrayContaining([Action.Download]),
6804
- }),
6805
- undefined,
6806
- );
6807
- });
6808
- });
6809
-
6810
- test('sends UpdateEmbedParams again every time the frame signals ready', async () => {
6811
- await renderAndGetSrc(withPostMessage(lbConfig));
6812
- signalFrameReady();
6813
- signalFrameReady();
6814
-
6815
- await executeAfterWait(() => {
6816
- const updateCalls = mockProcessTrigger.mock.calls.filter(
6817
- (call) => call[1] === HostEvent.UpdateEmbedParams,
6818
- );
6819
- expect(updateCalls).toHaveLength(2);
6820
- });
6821
- });
6822
-
6823
- test('does not send UpdateEmbedParams when the flag is not set', async () => {
6824
- await renderAndGetSrc(lbConfig);
6825
-
6826
- signalFrameReady();
6827
-
6828
- await executeAfterWait(() => {
6829
- const updateCalls = mockProcessTrigger.mock.calls.filter(
6830
- (call) => call[1] === HostEvent.UpdateEmbedParams,
6831
- );
6832
- expect(updateCalls).toHaveLength(0);
6833
- });
6834
- });
6835
-
6836
- test('does not double-send for a pre-rendered embed that is shown', async () => {
6837
- mockMessageChannel();
6838
- (window as any).ResizeObserver =
6839
- window.ResizeObserver ||
6840
- jest.fn().mockImplementation(() => ({
6841
- disconnect: jest.fn(),
6842
- observe: jest.fn(),
6843
- unobserve: jest.fn(),
6844
- }));
6845
-
6846
- const embed = new LiveboardEmbed(getRootEl(), {
6847
- ...defaultViewConfig,
6848
- ...withPostMessage(lbConfig),
6849
- preRenderId: 'send-config-post-message',
6850
- });
6851
- await embed.preRender();
6852
- await waitFor(() => !!getIFrameEl());
6853
-
6854
- signalFrameReady();
6855
- await executeAfterWait(() => {
6856
- expect(embed.isEmbedContainerLoaded).toBe(true);
6857
- });
6858
-
6859
- mockProcessTrigger.mockClear();
6860
- await embed.showPreRender();
6861
-
6862
- await executeAfterWait(() => {
6863
- const updateCalls = mockProcessTrigger.mock.calls.filter(
6864
- (call) => call[1] === HostEvent.UpdateEmbedParams,
6865
- );
6866
- expect(updateCalls).toHaveLength(1);
6867
- });
6868
- });
6869
- });