@thoughtspot/visual-embed-sdk 1.39.1 → 1.39.2-alpha.2

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 (190) hide show
  1. package/cjs/package.json +1 -1
  2. package/cjs/src/config.spec.js +9 -0
  3. package/cjs/src/config.spec.js.map +1 -1
  4. package/cjs/src/embed/app.d.ts +75 -15
  5. package/cjs/src/embed/app.d.ts.map +1 -1
  6. package/cjs/src/embed/app.js +69 -9
  7. package/cjs/src/embed/app.js.map +1 -1
  8. package/cjs/src/embed/app.spec.js +374 -12
  9. package/cjs/src/embed/app.spec.js.map +1 -1
  10. package/cjs/src/embed/bodyless-conversation.d.ts +19 -7
  11. package/cjs/src/embed/bodyless-conversation.d.ts.map +1 -1
  12. package/cjs/src/embed/bodyless-conversation.js +24 -4
  13. package/cjs/src/embed/bodyless-conversation.js.map +1 -1
  14. package/cjs/src/embed/bodyless-conversation.spec.js +8 -190
  15. package/cjs/src/embed/bodyless-conversation.spec.js.map +1 -1
  16. package/cjs/src/embed/conversation.spec.js +28 -0
  17. package/cjs/src/embed/conversation.spec.js.map +1 -1
  18. package/cjs/src/embed/embedConfig.d.ts +9 -7
  19. package/cjs/src/embed/embedConfig.d.ts.map +1 -1
  20. package/cjs/src/embed/embedConfig.js +9 -7
  21. package/cjs/src/embed/embedConfig.js.map +1 -1
  22. package/cjs/src/embed/liveboard.d.ts +56 -17
  23. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  24. package/cjs/src/embed/liveboard.js +48 -4
  25. package/cjs/src/embed/liveboard.js.map +1 -1
  26. package/cjs/src/embed/liveboard.spec.js +215 -11
  27. package/cjs/src/embed/liveboard.spec.js.map +1 -1
  28. package/cjs/src/embed/ts-embed.d.ts +5 -0
  29. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  30. package/cjs/src/embed/ts-embed.js +16 -1
  31. package/cjs/src/embed/ts-embed.js.map +1 -1
  32. package/cjs/src/embed/ts-embed.spec.js +164 -0
  33. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  34. package/cjs/src/errors.d.ts +1 -0
  35. package/cjs/src/errors.d.ts.map +1 -1
  36. package/cjs/src/errors.js +1 -0
  37. package/cjs/src/errors.js.map +1 -1
  38. package/cjs/src/index.d.ts +2 -2
  39. package/cjs/src/index.d.ts.map +1 -1
  40. package/cjs/src/index.js +2 -1
  41. package/cjs/src/index.js.map +1 -1
  42. package/cjs/src/react/all-types-export.d.ts +1 -1
  43. package/cjs/src/react/all-types-export.d.ts.map +1 -1
  44. package/cjs/src/react/all-types-export.js +3 -2
  45. package/cjs/src/react/all-types-export.js.map +1 -1
  46. package/cjs/src/react/index.d.ts +71 -20
  47. package/cjs/src/react/index.d.ts.map +1 -1
  48. package/cjs/src/react/index.js +79 -42
  49. package/cjs/src/react/index.js.map +1 -1
  50. package/cjs/src/react/index.spec.js +436 -100
  51. package/cjs/src/react/index.spec.js.map +1 -1
  52. package/cjs/src/types.d.ts +80 -6
  53. package/cjs/src/types.d.ts.map +1 -1
  54. package/cjs/src/types.js +45 -1
  55. package/cjs/src/types.js.map +1 -1
  56. package/cjs/src/utils/graphql/nlsService/conversation-service.d.ts.map +1 -1
  57. package/cjs/src/utils/graphql/nlsService/conversation-service.js +2 -0
  58. package/cjs/src/utils/graphql/nlsService/conversation-service.js.map +1 -1
  59. package/cjs/src/utils/processTrigger.js +2 -1
  60. package/cjs/src/utils/processTrigger.js.map +1 -1
  61. package/cjs/src/utils.d.ts +6 -0
  62. package/cjs/src/utils.d.ts.map +1 -1
  63. package/cjs/src/utils.js +23 -3
  64. package/cjs/src/utils.js.map +1 -1
  65. package/cjs/src/utils.spec.js +237 -1
  66. package/cjs/src/utils.spec.js.map +1 -1
  67. package/dist/{index-JaFaxrvQ.js → index-CmEQfuE3.js} +1 -1
  68. package/dist/index-DeFzsyFF.js +7371 -0
  69. package/dist/index-Dpf0rd6w.js +7371 -0
  70. package/dist/index-UuEbsISo.js +7447 -0
  71. package/dist/index-e3Uw3YFO.js +7371 -0
  72. package/dist/src/embed/app.d.ts +75 -15
  73. package/dist/src/embed/app.d.ts.map +1 -1
  74. package/dist/src/embed/bodyless-conversation.d.ts +19 -7
  75. package/dist/src/embed/bodyless-conversation.d.ts.map +1 -1
  76. package/dist/src/embed/embedConfig.d.ts +9 -7
  77. package/dist/src/embed/embedConfig.d.ts.map +1 -1
  78. package/dist/src/embed/liveboard.d.ts +56 -17
  79. package/dist/src/embed/liveboard.d.ts.map +1 -1
  80. package/dist/src/embed/ts-embed.d.ts +5 -0
  81. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  82. package/dist/src/errors.d.ts +1 -0
  83. package/dist/src/errors.d.ts.map +1 -1
  84. package/dist/src/index.d.ts +2 -2
  85. package/dist/src/index.d.ts.map +1 -1
  86. package/dist/src/react/all-types-export.d.ts +1 -1
  87. package/dist/src/react/all-types-export.d.ts.map +1 -1
  88. package/dist/src/react/index.d.ts +71 -20
  89. package/dist/src/react/index.d.ts.map +1 -1
  90. package/dist/src/types.d.ts +80 -6
  91. package/dist/src/types.d.ts.map +1 -1
  92. package/dist/src/utils/graphql/nlsService/conversation-service.d.ts.map +1 -1
  93. package/dist/src/utils.d.ts +6 -0
  94. package/dist/src/utils.d.ts.map +1 -1
  95. package/dist/tsembed-react.es.js +320 -78
  96. package/dist/tsembed-react.js +320 -76
  97. package/dist/tsembed.es.js +238 -31
  98. package/dist/tsembed.js +236 -29
  99. package/dist/visual-embed-sdk-react-full.d.ts +288 -72
  100. package/dist/visual-embed-sdk-react.d.ts +288 -72
  101. package/dist/visual-embed-sdk.d.ts +218 -53
  102. package/lib/package.json +1 -1
  103. package/lib/src/config.spec.js +9 -0
  104. package/lib/src/config.spec.js.map +1 -1
  105. package/lib/src/embed/app.d.ts +75 -15
  106. package/lib/src/embed/app.d.ts.map +1 -1
  107. package/lib/src/embed/app.js +69 -9
  108. package/lib/src/embed/app.js.map +1 -1
  109. package/lib/src/embed/app.spec.js +376 -14
  110. package/lib/src/embed/app.spec.js.map +1 -1
  111. package/lib/src/embed/bodyless-conversation.d.ts +19 -7
  112. package/lib/src/embed/bodyless-conversation.d.ts.map +1 -1
  113. package/lib/src/embed/bodyless-conversation.js +23 -4
  114. package/lib/src/embed/bodyless-conversation.js.map +1 -1
  115. package/lib/src/embed/bodyless-conversation.spec.js +9 -191
  116. package/lib/src/embed/bodyless-conversation.spec.js.map +1 -1
  117. package/lib/src/embed/conversation.spec.js +30 -2
  118. package/lib/src/embed/conversation.spec.js.map +1 -1
  119. package/lib/src/embed/embedConfig.d.ts +9 -7
  120. package/lib/src/embed/embedConfig.d.ts.map +1 -1
  121. package/lib/src/embed/embedConfig.js +9 -7
  122. package/lib/src/embed/embedConfig.js.map +1 -1
  123. package/lib/src/embed/liveboard.d.ts +56 -17
  124. package/lib/src/embed/liveboard.d.ts.map +1 -1
  125. package/lib/src/embed/liveboard.js +49 -5
  126. package/lib/src/embed/liveboard.js.map +1 -1
  127. package/lib/src/embed/liveboard.spec.js +215 -11
  128. package/lib/src/embed/liveboard.spec.js.map +1 -1
  129. package/lib/src/embed/ts-embed.d.ts +5 -0
  130. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  131. package/lib/src/embed/ts-embed.js +16 -1
  132. package/lib/src/embed/ts-embed.js.map +1 -1
  133. package/lib/src/embed/ts-embed.spec.js +164 -0
  134. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  135. package/lib/src/errors.d.ts +1 -0
  136. package/lib/src/errors.d.ts.map +1 -1
  137. package/lib/src/errors.js +1 -0
  138. package/lib/src/errors.js.map +1 -1
  139. package/lib/src/index.d.ts +2 -2
  140. package/lib/src/index.d.ts.map +1 -1
  141. package/lib/src/index.js +2 -2
  142. package/lib/src/index.js.map +1 -1
  143. package/lib/src/react/all-types-export.d.ts +1 -1
  144. package/lib/src/react/all-types-export.d.ts.map +1 -1
  145. package/lib/src/react/all-types-export.js +1 -1
  146. package/lib/src/react/all-types-export.js.map +1 -1
  147. package/lib/src/react/index.d.ts +71 -20
  148. package/lib/src/react/index.d.ts.map +1 -1
  149. package/lib/src/react/index.js +79 -43
  150. package/lib/src/react/index.js.map +1 -1
  151. package/lib/src/react/index.spec.js +439 -103
  152. package/lib/src/react/index.spec.js.map +1 -1
  153. package/lib/src/types.d.ts +80 -6
  154. package/lib/src/types.d.ts.map +1 -1
  155. package/lib/src/types.js +45 -1
  156. package/lib/src/types.js.map +1 -1
  157. package/lib/src/utils/graphql/nlsService/conversation-service.d.ts.map +1 -1
  158. package/lib/src/utils/graphql/nlsService/conversation-service.js +2 -0
  159. package/lib/src/utils/graphql/nlsService/conversation-service.js.map +1 -1
  160. package/lib/src/utils/processTrigger.js +2 -1
  161. package/lib/src/utils/processTrigger.js.map +1 -1
  162. package/lib/src/utils.d.ts +6 -0
  163. package/lib/src/utils.d.ts.map +1 -1
  164. package/lib/src/utils.js +21 -2
  165. package/lib/src/utils.js.map +1 -1
  166. package/lib/src/utils.spec.js +238 -2
  167. package/lib/src/utils.spec.js.map +1 -1
  168. package/lib/src/visual-embed-sdk.d.ts +219 -54
  169. package/package.json +1 -1
  170. package/src/config.spec.ts +11 -0
  171. package/src/embed/app.spec.ts +479 -26
  172. package/src/embed/app.ts +133 -27
  173. package/src/embed/bodyless-conversation.spec.ts +9 -203
  174. package/src/embed/bodyless-conversation.ts +24 -10
  175. package/src/embed/conversation.spec.ts +40 -2
  176. package/src/embed/embedConfig.ts +10 -8
  177. package/src/embed/liveboard.spec.ts +256 -5
  178. package/src/embed/liveboard.ts +99 -27
  179. package/src/embed/ts-embed.spec.ts +225 -0
  180. package/src/embed/ts-embed.ts +19 -0
  181. package/src/errors.ts +1 -0
  182. package/src/index.ts +2 -0
  183. package/src/react/all-types-export.ts +2 -1
  184. package/src/react/index.spec.tsx +556 -157
  185. package/src/react/index.tsx +117 -51
  186. package/src/types.ts +117 -43
  187. package/src/utils/graphql/nlsService/conversation-service.ts +2 -0
  188. package/src/utils/processTrigger.ts +1 -1
  189. package/src/utils.spec.ts +279 -2
  190. package/src/utils.ts +28 -2
@@ -10,13 +10,14 @@ const configKey = 'embedConfig';
10
10
  *
11
11
  * @example
12
12
  * ```js
13
+ * import { getInitConfig } from '@thoughtspot/visual-embed-sdk';
14
+ * // Call the getInitConfig method to retrieve the embed configuration
13
15
  * const config = getInitConfig();
16
+ * // Log the configuration settings
14
17
  * console.log(config);
15
18
  * ```
16
- * @example
17
- *
18
- * Returns the `EmbedConfig` object, which
19
- * contains the configuration settings used to
19
+ * Returns the link:https://developers.thoughtspot.com/docs/Interface_EmbedConfig[EmbedConfig]
20
+ * object, which contains the configuration settings used to
20
21
  * initialize the SDK, including the following:
21
22
  *
22
23
  * - `thoughtSpotHost` - ThoughtSpot host URL
@@ -24,10 +25,11 @@ const configKey = 'embedConfig';
24
25
  * `AuthServerCookieless` for `AuthType.TrustedAuthTokenCookieless`
25
26
  * - `customizations` - Style, text, and icon customization settings
26
27
  * that were applied during the SDK initialization.
27
- *
28
- * For a comprehensive list of embed configuration settings,
29
- * see link:https://developers.thoughtspot.com/docs/Interface_EmbedConfig[Developer Documentation].
30
28
  *
29
+ * The following JSON output shows the embed configuration
30
+ * settings returned from the code in the previous example:
31
+ *
32
+ * @example
31
33
  * ```json
32
34
  * {
33
35
  * "thoughtSpotHost": "https://{ThoughtSpot-Host}",
@@ -49,7 +51,7 @@ const configKey = 'embedConfig';
49
51
  * "authTriggerContainer": "#your-own-div"
50
52
  * }
51
53
  * ```
52
- * @version SDK: 1.19.0 | ThoughtSpot: 9.0.0.cl, 9.0.1.cl, and later
54
+ * @version SDK: 1.19.0 | ThoughtSpot: 9.0.0.cl, 9.0.1.sw, and later
53
55
  * @group Global methods
54
56
  */
55
57
  export const getEmbedConfig = (): EmbedConfig => getValueFromWindow(configKey) || ({} as any);
@@ -347,7 +347,7 @@ describe('Liveboard/viz embed tests', () => {
347
347
  await executeAfterWait(() => {
348
348
  expectUrlMatchesWithParams(
349
349
  getIFrameSrc(),
350
- `http://${thoughtSpotHost}/?embedApp=true${defaultParams}&coverAndFilterOptionInPDF=true${prefixParams}#/embed/viz/${liveboardId}`,
350
+ `http://${thoughtSpotHost}/?embedApp=true${defaultParams}&arePdfCoverFilterPageCheckboxesEnabled=true${prefixParams}#/embed/viz/${liveboardId}`,
351
351
  );
352
352
  });
353
353
  });
@@ -362,7 +362,7 @@ describe('Liveboard/viz embed tests', () => {
362
362
  await executeAfterWait(() => {
363
363
  expectUrlMatchesWithParams(
364
364
  getIFrameSrc(),
365
- `http://${thoughtSpotHost}/?embedApp=true${defaultParams}&coverAndFilterOptionInPDF=false&${prefixParams}#/embed/viz/${liveboardId}`,
365
+ `http://${thoughtSpotHost}/?embedApp=true${defaultParams}&arePdfCoverFilterPageCheckboxesEnabled=false&${prefixParams}#/embed/viz/${liveboardId}`,
366
366
  );
367
367
  });
368
368
  });
@@ -770,8 +770,9 @@ describe('Liveboard/viz embed tests', () => {
770
770
  });
771
771
 
772
772
  let resizeObserverCb: any;
773
- (window as any).ResizeObserver = window.ResizeObserver
774
- || jest.fn().mockImplementation((resizeObserverCbParam: any) => {
773
+ (window as any).ResizeObserver =
774
+ window.ResizeObserver ||
775
+ jest.fn().mockImplementation((resizeObserverCbParam: any) => {
775
776
  resizeObserverCb = resizeObserverCbParam;
776
777
  return {
777
778
  disconnect: jest.fn(),
@@ -785,7 +786,7 @@ describe('Liveboard/viz embed tests', () => {
785
786
  await waitFor(() => !!getIFrameEl());
786
787
 
787
788
  const ts = '__tsEmbed';
788
- expect(document.getElementById(libEmbed.getPreRenderIds().wrapper)[ts]).toEqual(
789
+ expect((document.getElementById(libEmbed.getPreRenderIds().wrapper) as any)[ts]).toEqual(
789
790
  libEmbed,
790
791
  );
791
792
 
@@ -813,6 +814,256 @@ describe('Liveboard/viz embed tests', () => {
813
814
  });
814
815
  });
815
816
 
817
+ describe('LazyLoadingForFullHeight functionality', () => {
818
+ let mockIFrame: HTMLIFrameElement;
819
+
820
+ beforeEach(() => {
821
+ mockIFrame = document.createElement('iframe');
822
+ mockIFrame.getBoundingClientRect = jest.fn().mockReturnValue({
823
+ top: 100,
824
+ left: 150,
825
+ bottom: 600,
826
+ right: 800,
827
+ width: 650,
828
+ height: 500,
829
+ });
830
+ jest.spyOn(document, 'createElement').mockImplementation((tagName) => {
831
+ if (tagName === 'iframe') {
832
+ return mockIFrame;
833
+ }
834
+ return document.createElement(tagName);
835
+ });
836
+ });
837
+
838
+ afterEach(() => {
839
+ jest.restoreAllMocks();
840
+ });
841
+
842
+ test('should set lazyLoadingMargin parameter when provided', async () => {
843
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
844
+ ...defaultViewConfig,
845
+ liveboardId,
846
+ fullHeight: true,
847
+ lazyLoadingForFullHeight: true,
848
+ lazyLoadingMargin: '100px 0px',
849
+ } as LiveboardViewConfig);
850
+
851
+ await liveboardEmbed.render();
852
+
853
+ await executeAfterWait(() => {
854
+ const iframeSrc = getIFrameSrc();
855
+ expect(iframeSrc).toContain('isLazyLoadingForEmbedEnabled=true');
856
+ expect(iframeSrc).toContain('isFullHeightPinboard=true');
857
+ expect(iframeSrc).toContain('rootMarginForLazyLoad=100px%200px');
858
+ }, 100);
859
+ });
860
+
861
+ test('should set isLazyLoadingForEmbedEnabled=true when both fullHeight and lazyLoadingForFullHeight are enabled', async () => {
862
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
863
+ ...defaultViewConfig,
864
+ liveboardId,
865
+ fullHeight: true,
866
+ lazyLoadingForFullHeight: true,
867
+ } as LiveboardViewConfig);
868
+
869
+ await liveboardEmbed.render();
870
+
871
+ await executeAfterWait(() => {
872
+ const iframeSrc = getIFrameSrc();
873
+ expect(iframeSrc).toContain('isLazyLoadingForEmbedEnabled=true');
874
+ expect(iframeSrc).toContain('isFullHeightPinboard=true');
875
+ }, 100);
876
+ });
877
+
878
+ test('should not set lazyLoadingForEmbed when lazyLoadingForFullHeight is enabled but fullHeight is false', async () => {
879
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
880
+ ...defaultViewConfig,
881
+ liveboardId,
882
+ fullHeight: false,
883
+ lazyLoadingForFullHeight: true,
884
+ } as LiveboardViewConfig);
885
+
886
+ await liveboardEmbed.render();
887
+
888
+ await executeAfterWait(() => {
889
+ const iframeSrc = getIFrameSrc();
890
+ expect(iframeSrc).not.toContain('isLazyLoadingForEmbedEnabled=true');
891
+ expect(iframeSrc).not.toContain('isFullHeightPinboard=true');
892
+ }, 100);
893
+ });
894
+
895
+ test('should not set isLazyLoadingForEmbedEnabled when fullHeight is true but lazyLoadingForFullHeight is false', async () => {
896
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
897
+ ...defaultViewConfig,
898
+ liveboardId,
899
+ fullHeight: true,
900
+ lazyLoadingForFullHeight: false,
901
+ } as LiveboardViewConfig);
902
+
903
+ await liveboardEmbed.render();
904
+
905
+ await executeAfterWait(() => {
906
+ const iframeSrc = getIFrameSrc();
907
+ expect(iframeSrc).not.toContain('isLazyLoadingForEmbedEnabled=true');
908
+ expect(iframeSrc).toContain('isFullHeightPinboard=true');
909
+ }, 100);
910
+ });
911
+
912
+ test('should register event handlers to adjust iframe height', async () => {
913
+ const onSpy = jest.spyOn(LiveboardEmbed.prototype, 'on');
914
+
915
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
916
+ ...defaultViewConfig,
917
+ liveboardId,
918
+ fullHeight: true,
919
+ lazyLoadingForFullHeight: true,
920
+ } as LiveboardViewConfig);
921
+
922
+ await liveboardEmbed.render();
923
+
924
+ await executeAfterWait(() => {
925
+ expect(onSpy).toHaveBeenCalledWith(EmbedEvent.EmbedHeight, expect.anything());
926
+ expect(onSpy).toHaveBeenCalledWith(EmbedEvent.RouteChange, expect.anything());
927
+ expect(onSpy).toHaveBeenCalledWith(EmbedEvent.EmbedIframeCenter, expect.anything());
928
+ expect(onSpy).toHaveBeenCalledWith(EmbedEvent.RequestVisibleEmbedCoordinates, expect.anything());
929
+ }, 100);
930
+ });
931
+
932
+ test('should send correct visible data when RequestVisibleEmbedCoordinates is triggered', async () => {
933
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
934
+ ...defaultViewConfig,
935
+ liveboardId,
936
+ fullHeight: true,
937
+ lazyLoadingForFullHeight: true,
938
+ } as LiveboardViewConfig);
939
+
940
+ const mockTrigger = jest.spyOn(liveboardEmbed, 'trigger');
941
+
942
+ await liveboardEmbed.render();
943
+
944
+ // Trigger the lazy load data calculation
945
+ (liveboardEmbed as any).sendFullHeightLazyLoadData();
946
+
947
+ expect(mockTrigger).toHaveBeenCalledWith(HostEvent.VisibleEmbedCoordinates, {
948
+ top: 0,
949
+ height: 500,
950
+ left: 0,
951
+ width: 650,
952
+ });
953
+ });
954
+
955
+ test('should calculate correct visible data for partially visible full height element', async () => {
956
+ mockIFrame.getBoundingClientRect = jest.fn().mockReturnValue({
957
+ top: -50,
958
+ left: -30,
959
+ bottom: 700,
960
+ right: 1024,
961
+ width: 1054,
962
+ height: 750,
963
+ });
964
+
965
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
966
+ ...defaultViewConfig,
967
+ liveboardId,
968
+ fullHeight: true,
969
+ lazyLoadingForFullHeight: true,
970
+ } as LiveboardViewConfig);
971
+
972
+ const mockTrigger = jest.spyOn(liveboardEmbed, 'trigger');
973
+
974
+ await liveboardEmbed.render();
975
+
976
+ // Trigger the lazy load data calculation
977
+ (liveboardEmbed as any).sendFullHeightLazyLoadData();
978
+
979
+ expect(mockTrigger).toHaveBeenCalledWith(HostEvent.VisibleEmbedCoordinates, {
980
+ top: 50,
981
+ height: 700,
982
+ left: 30,
983
+ width: 1024,
984
+ });
985
+ });
986
+
987
+ test('should add window event listeners for resize and scroll when fullHeight and lazyLoadingForFullHeight are enabled', async () => {
988
+ const addEventListenerSpy = jest.spyOn(window, 'addEventListener');
989
+
990
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
991
+ ...defaultViewConfig,
992
+ liveboardId,
993
+ fullHeight: true,
994
+ lazyLoadingForFullHeight: true,
995
+ } as LiveboardViewConfig);
996
+
997
+ await liveboardEmbed.render();
998
+
999
+ expect(addEventListenerSpy).toHaveBeenCalledWith('resize', expect.anything());
1000
+ expect(addEventListenerSpy).toHaveBeenCalledWith('scroll', expect.anything());
1001
+
1002
+ addEventListenerSpy.mockRestore();
1003
+ });
1004
+
1005
+ test('should remove window event listeners on destroy when fullHeight and lazyLoadingForFullHeight are enabled', async () => {
1006
+ const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener');
1007
+
1008
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1009
+ ...defaultViewConfig,
1010
+ liveboardId,
1011
+ fullHeight: true,
1012
+ lazyLoadingForFullHeight: true,
1013
+ } as LiveboardViewConfig);
1014
+
1015
+ await liveboardEmbed.render();
1016
+ liveboardEmbed.destroy();
1017
+
1018
+ expect(removeEventListenerSpy).toHaveBeenCalledWith('resize', expect.anything());
1019
+ expect(removeEventListenerSpy).toHaveBeenCalledWith('scroll', expect.anything());
1020
+
1021
+ removeEventListenerSpy.mockRestore();
1022
+ });
1023
+
1024
+ test('should handle RequestVisibleEmbedCoordinates event and respond with correct data', async () => {
1025
+ // Mock the iframe element
1026
+ mockIFrame.getBoundingClientRect = jest.fn().mockReturnValue({
1027
+ top: 100,
1028
+ left: 150,
1029
+ bottom: 600,
1030
+ right: 800,
1031
+ width: 650,
1032
+ height: 500,
1033
+ });
1034
+ Object.defineProperty(mockIFrame, 'scrollHeight', { value: 500 });
1035
+
1036
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1037
+ ...defaultViewConfig,
1038
+ liveboardId,
1039
+ fullHeight: true,
1040
+ lazyLoadingForFullHeight: true,
1041
+ } as LiveboardViewConfig);
1042
+
1043
+ // Set the iframe before render
1044
+ (liveboardEmbed as any).iFrame = mockIFrame;
1045
+
1046
+ await liveboardEmbed.render();
1047
+
1048
+ // Create a mock responder function
1049
+ const mockResponder = jest.fn();
1050
+
1051
+ // Trigger the handler directly
1052
+ (liveboardEmbed as any).requestVisibleEmbedCoordinatesHandler({}, mockResponder);
1053
+
1054
+ // Verify the responder was called with the correct data
1055
+ expect(mockResponder).toHaveBeenCalledWith({
1056
+ type: EmbedEvent.RequestVisibleEmbedCoordinates,
1057
+ data: {
1058
+ top: 0,
1059
+ height: 500,
1060
+ left: 0,
1061
+ width: 650,
1062
+ },
1063
+ });
1064
+ });
1065
+ });
1066
+
816
1067
  describe('Host events for liveborad', () => {
817
1068
  test('Host event with empty param', async () => {
818
1069
  const mockProcessTrigger = jest.spyOn(tsEmbed.TsEmbed.prototype, 'trigger');
@@ -21,7 +21,7 @@ import {
21
21
  BaseViewConfig,
22
22
  LiveboardAppEmbedViewConfig,
23
23
  } from '../types';
24
- import { getQueryParamString, isUndefined } from '../utils';
24
+ import { calculateVisibleElementData, getQueryParamString, isUndefined } from '../utils';
25
25
  import { getAuthPromise } from './base';
26
26
  import { TsEmbed, V1Embed } from './ts-embed';
27
27
  import { addPreviewStylesIfNotPresent } from '../utils/global-styles';
@@ -269,24 +269,6 @@ export interface LiveboardViewConfig extends BaseViewConfig, LiveboardOtherViewC
269
269
  * })
270
270
  */
271
271
  dataSourceId?: string;
272
-
273
- /**
274
- * This flag is for show/hide checkboxes for include or exclude
275
- * cover page and filters in the Liveboard PDF.
276
- *
277
- * Supported embed types: `LiveboardEmbed`
278
- * @version SDK: 1.37.0 | ThoughtSpot:10.8.0.cl
279
- * @default true
280
- * Supported embed types: `LiveboardEmbed`
281
- * @example
282
- * ```js
283
- * const embed = new LiveboardEmbed('#tsEmbed', {
284
- * ... //other embed view config
285
- * coverAndFilterOptionInPDF: false,
286
- * })
287
- * ```
288
- */
289
- coverAndFilterOptionInPDF?: boolean;
290
272
  /**
291
273
  * The list of tab IDs to hide from the embedded.
292
274
  * This Tabs will be hidden from their respective LBs.
@@ -342,6 +324,47 @@ export interface LiveboardViewConfig extends BaseViewConfig, LiveboardOtherViewC
342
324
  * ```
343
325
  */
344
326
  isLiveboardStylingAndGroupingEnabled?: boolean;
327
+ /**
328
+ * This flag is used to enable the full height lazy load data.
329
+ *
330
+ * @example
331
+ * ```js
332
+ * const embed = new LiveboardEmbed('#embed-container', {
333
+ * // ...other options
334
+ * fullHeight: true,
335
+ * lazyLoadingForFullHeight: true,
336
+ * })
337
+ * ```
338
+ *
339
+ * @type {boolean}
340
+ * @default false
341
+ * @version SDK: 1.39.0 | ThoughtSpot:10.10.0.cl
342
+ */
343
+ lazyLoadingForFullHeight?: boolean;
344
+
345
+ /**
346
+ * The margin to be used for lazy loading.
347
+ *
348
+ * For example, if the margin is set to '10px',
349
+ * the visualization will be loaded 10px before the its top edge is visible in the
350
+ * viewport.
351
+ *
352
+ * The format is similar to CSS margin.
353
+ *
354
+ * @example
355
+ * ```js
356
+ * const embed = new LiveboardEmbed('#embed-container', {
357
+ * // ...other options
358
+ * fullHeight: true,
359
+ * lazyLoadingForFullHeight: true,
360
+ * // Using 0px, the visualization will be only loaded when its visible in the viewport.
361
+ * lazyLoadingMargin: '0px',
362
+ * })
363
+ * ```
364
+ * @type {string}
365
+ * @version SDK: 1.39.0 | ThoughtSpot:10.10.0.cl
366
+ */
367
+ lazyLoadingMargin?: string;
345
368
  }
346
369
 
347
370
  /**
@@ -364,19 +387,20 @@ export class LiveboardEmbed extends V1Embed {
364
387
 
365
388
  private defaultHeight = 500;
366
389
 
367
-
390
+
368
391
  constructor(domSelector: DOMSelector, viewConfig: LiveboardViewConfig) {
369
392
  viewConfig.embedComponentType = 'LiveboardEmbed';
370
393
  super(domSelector, viewConfig);
371
394
  if (this.viewConfig.fullHeight === true) {
372
395
  if (this.viewConfig.vizId) {
373
396
  logger.warn('Full height is currently only supported for Liveboard embeds.' +
374
- 'Using full height with vizId might lead to unexpected behavior.');
397
+ 'Using full height with vizId might lead to unexpected behavior.');
375
398
  }
376
399
 
377
400
  this.on(EmbedEvent.RouteChange, this.setIframeHeightForNonEmbedLiveboard);
378
401
  this.on(EmbedEvent.EmbedHeight, this.updateIFrameHeight);
379
402
  this.on(EmbedEvent.EmbedIframeCenter, this.embedIframeCenter);
403
+ this.on(EmbedEvent.RequestVisibleEmbedCoordinates, this.requestVisibleEmbedCoordinatesHandler);
380
404
  }
381
405
  }
382
406
 
@@ -385,7 +409,7 @@ export class LiveboardEmbed extends V1Embed {
385
409
  * embedded Liveboard or visualization.
386
410
  */
387
411
  protected getEmbedParams() {
388
- let params = {};
412
+ let params: any = {};
389
413
  params = this.getBaseQueryParams(params);
390
414
  const {
391
415
  enableVizTransformations,
@@ -411,7 +435,7 @@ export class LiveboardEmbed extends V1Embed {
411
435
  oAuthPollingInterval,
412
436
  isForceRedirect,
413
437
  dataSourceId,
414
- coverAndFilterOptionInPDF,
438
+ coverAndFilterOptionInPDF = false,
415
439
  isLiveboardStylingAndGroupingEnabled,
416
440
  } = this.viewConfig;
417
441
 
@@ -420,6 +444,10 @@ export class LiveboardEmbed extends V1Embed {
420
444
 
421
445
  if (fullHeight === true) {
422
446
  params[Param.fullHeight] = true;
447
+ if (this.viewConfig.lazyLoadingForFullHeight) {
448
+ params[Param.IsLazyLoadingForEmbedEnabled] = true;
449
+ params[Param.RootMarginForLazyLoad] = this.viewConfig.lazyLoadingMargin;
450
+ }
423
451
  }
424
452
  if (defaultHeight) {
425
453
  this.defaultHeight = defaultHeight;
@@ -471,9 +499,6 @@ export class LiveboardEmbed extends V1Embed {
471
499
  params[Param.DataSourceId] = dataSourceId;
472
500
  }
473
501
 
474
- if (coverAndFilterOptionInPDF !== undefined) {
475
- params[Param.CoverAndFilterOptionInPDF] = coverAndFilterOptionInPDF;
476
- }
477
502
 
478
503
  if (isLiveboardStylingAndGroupingEnabled !== undefined) {
479
504
  params[Param.IsLiveboardStylingAndGroupingEnabled] = isLiveboardStylingAndGroupingEnabled;
@@ -486,6 +511,7 @@ export class LiveboardEmbed extends V1Embed {
486
511
  params[Param.HideIrrelevantFiltersInTab] = hideIrrelevantChipsInLiveboardTabs;
487
512
  params[Param.DataPanelV2Enabled] = dataPanelV2;
488
513
  params[Param.EnableCustomColumnGroups] = enableCustomColumnGroups;
514
+ params[Param.CoverAndFilterOptionInPDF] = coverAndFilterOptionInPDF;
489
515
  const queryParams = getQueryParamString(params, true);
490
516
 
491
517
  return queryParams;
@@ -504,6 +530,23 @@ export class LiveboardEmbed extends V1Embed {
504
530
  return suffix;
505
531
  }
506
532
 
533
+ private sendFullHeightLazyLoadData = () => {
534
+ const data = calculateVisibleElementData(this.iFrame);
535
+ this.trigger(HostEvent.VisibleEmbedCoordinates, data);
536
+ }
537
+
538
+ /**
539
+ * This is a handler for the RequestVisibleEmbedCoordinates event.
540
+ * It is used to send the visible coordinates data to the host application.
541
+ * @param data The event payload
542
+ * @param responder The responder function
543
+ */
544
+ private requestVisibleEmbedCoordinatesHandler = (data: MessagePayload, responder: any) => {
545
+ logger.info('Sending RequestVisibleEmbedCoordinates', data);
546
+ const visibleCoordinatesData = calculateVisibleElementData(this.iFrame);
547
+ responder({ type: EmbedEvent.RequestVisibleEmbedCoordinates, data: visibleCoordinatesData });
548
+ }
549
+
507
550
  /**
508
551
  * Construct the URL of the embedded ThoughtSpot Liveboard or visualization
509
552
  * to be loaded within the iFrame.
@@ -529,6 +572,7 @@ export class LiveboardEmbed extends V1Embed {
529
572
  */
530
573
  private updateIFrameHeight = (data: MessagePayload) => {
531
574
  this.setIFrameHeight(Math.max(data.data, this.defaultHeight));
575
+ this.sendFullHeightLazyLoadData();
532
576
  };
533
577
 
534
578
  private embedIframeCenter = (data: MessagePayload, responder: any) => {
@@ -602,7 +646,7 @@ export class LiveboardEmbed extends V1Embed {
602
646
  }
603
647
 
604
648
  protected beforePrerenderVisible(): void {
605
- const embedObj = this.insertedDomEl?.[this.embedNodeKey] as LiveboardEmbed;
649
+ const embedObj = (this.insertedDomEl as any)?.[this.embedNodeKey] as LiveboardEmbed;
606
650
 
607
651
  if (isUndefined(embedObj)) return;
608
652
 
@@ -642,6 +686,33 @@ export class LiveboardEmbed extends V1Embed {
642
686
  }
643
687
  return super.trigger(messageType, dataWithVizId);
644
688
  }
689
+ /**
690
+ * Destroys the ThoughtSpot embed, and remove any nodes from the DOM.
691
+ * @version SDK: 1.39.0 | ThoughtSpot: 10.10.0.cl
692
+ */
693
+ public destroy() {
694
+ super.destroy();
695
+ this.unregisterLazyLoadEvents();
696
+ }
697
+
698
+ private postRender() {
699
+ this.registerLazyLoadEvents();
700
+ }
701
+
702
+ private registerLazyLoadEvents() {
703
+ if (this.viewConfig.fullHeight && this.viewConfig.lazyLoadingForFullHeight) {
704
+ // TODO: Use passive: true, install modernizr to check for passive
705
+ window.addEventListener('resize', this.sendFullHeightLazyLoadData);
706
+ window.addEventListener('scroll', this.sendFullHeightLazyLoadData);
707
+ }
708
+ }
709
+
710
+ private unregisterLazyLoadEvents() {
711
+ if (this.viewConfig.fullHeight && this.viewConfig.lazyLoadingForFullHeight) {
712
+ window.removeEventListener('resize', this.sendFullHeightLazyLoadData);
713
+ window.removeEventListener('scroll', this.sendFullHeightLazyLoadData);
714
+ }
715
+ }
645
716
 
646
717
  /**
647
718
  * Render an embedded ThoughtSpot Liveboard or visualization
@@ -655,6 +726,7 @@ export class LiveboardEmbed extends V1Embed {
655
726
  await this.renderV1Embed(src);
656
727
  this.showPreviewLoader();
657
728
 
729
+ this.postRender();
658
730
  return this;
659
731
  }
660
732