@thoughtspot/visual-embed-sdk 1.39.2 → 1.40.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 (173) 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 +378 -15
  9. package/cjs/src/embed/app.spec.js.map +1 -1
  10. package/cjs/src/embed/bodyless-conversation.d.ts +23 -7
  11. package/cjs/src/embed/bodyless-conversation.d.ts.map +1 -1
  12. package/cjs/src/embed/bodyless-conversation.js +31 -5
  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 +218 -11
  27. package/cjs/src/embed/liveboard.spec.js.map +1 -1
  28. package/cjs/src/errors.d.ts +1 -0
  29. package/cjs/src/errors.d.ts.map +1 -1
  30. package/cjs/src/errors.js +1 -0
  31. package/cjs/src/errors.js.map +1 -1
  32. package/cjs/src/index.d.ts +2 -2
  33. package/cjs/src/index.d.ts.map +1 -1
  34. package/cjs/src/index.js +2 -1
  35. package/cjs/src/index.js.map +1 -1
  36. package/cjs/src/react/all-types-export.d.ts +1 -1
  37. package/cjs/src/react/all-types-export.d.ts.map +1 -1
  38. package/cjs/src/react/all-types-export.js +3 -2
  39. package/cjs/src/react/all-types-export.js.map +1 -1
  40. package/cjs/src/react/index.d.ts +73 -20
  41. package/cjs/src/react/index.d.ts.map +1 -1
  42. package/cjs/src/react/index.js +79 -42
  43. package/cjs/src/react/index.js.map +1 -1
  44. package/cjs/src/react/index.spec.js +438 -100
  45. package/cjs/src/react/index.spec.js.map +1 -1
  46. package/cjs/src/types.d.ts +331 -13
  47. package/cjs/src/types.d.ts.map +1 -1
  48. package/cjs/src/types.js +296 -8
  49. package/cjs/src/types.js.map +1 -1
  50. package/cjs/src/utils/global-styles.js +1 -1
  51. package/cjs/src/utils/graphql/nlsService/conversation-service.d.ts.map +1 -1
  52. package/cjs/src/utils/graphql/nlsService/conversation-service.js +7 -1
  53. package/cjs/src/utils/graphql/nlsService/conversation-service.js.map +1 -1
  54. package/cjs/src/utils/processTrigger.js +2 -1
  55. package/cjs/src/utils/processTrigger.js.map +1 -1
  56. package/cjs/src/utils.d.ts +6 -0
  57. package/cjs/src/utils.d.ts.map +1 -1
  58. package/cjs/src/utils.js +23 -3
  59. package/cjs/src/utils.js.map +1 -1
  60. package/cjs/src/utils.spec.js +237 -1
  61. package/cjs/src/utils.spec.js.map +1 -1
  62. package/dist/{index-CmEQfuE3.js → index-CAEHQGLc.js} +1 -1
  63. package/dist/src/embed/app.d.ts +75 -15
  64. package/dist/src/embed/app.d.ts.map +1 -1
  65. package/dist/src/embed/bodyless-conversation.d.ts +23 -7
  66. package/dist/src/embed/bodyless-conversation.d.ts.map +1 -1
  67. package/dist/src/embed/embedConfig.d.ts +9 -7
  68. package/dist/src/embed/embedConfig.d.ts.map +1 -1
  69. package/dist/src/embed/liveboard.d.ts +56 -17
  70. package/dist/src/embed/liveboard.d.ts.map +1 -1
  71. package/dist/src/errors.d.ts +1 -0
  72. package/dist/src/errors.d.ts.map +1 -1
  73. package/dist/src/index.d.ts +2 -2
  74. package/dist/src/index.d.ts.map +1 -1
  75. package/dist/src/react/all-types-export.d.ts +1 -1
  76. package/dist/src/react/all-types-export.d.ts.map +1 -1
  77. package/dist/src/react/index.d.ts +73 -20
  78. package/dist/src/react/index.d.ts.map +1 -1
  79. package/dist/src/types.d.ts +331 -13
  80. package/dist/src/types.d.ts.map +1 -1
  81. package/dist/src/utils/graphql/nlsService/conversation-service.d.ts.map +1 -1
  82. package/dist/src/utils.d.ts +6 -0
  83. package/dist/src/utils.d.ts.map +1 -1
  84. package/dist/tsembed-react.es.js +568 -87
  85. package/dist/tsembed-react.js +568 -85
  86. package/dist/tsembed.es.js +486 -40
  87. package/dist/tsembed.js +484 -38
  88. package/dist/visual-embed-sdk-react-full.d.ts +545 -79
  89. package/dist/visual-embed-sdk-react.d.ts +545 -79
  90. package/dist/visual-embed-sdk.d.ts +473 -60
  91. package/lib/package.json +1 -1
  92. package/lib/src/config.spec.js +9 -0
  93. package/lib/src/config.spec.js.map +1 -1
  94. package/lib/src/embed/app.d.ts +75 -15
  95. package/lib/src/embed/app.d.ts.map +1 -1
  96. package/lib/src/embed/app.js +69 -9
  97. package/lib/src/embed/app.js.map +1 -1
  98. package/lib/src/embed/app.spec.js +380 -17
  99. package/lib/src/embed/app.spec.js.map +1 -1
  100. package/lib/src/embed/bodyless-conversation.d.ts +23 -7
  101. package/lib/src/embed/bodyless-conversation.d.ts.map +1 -1
  102. package/lib/src/embed/bodyless-conversation.js +30 -5
  103. package/lib/src/embed/bodyless-conversation.js.map +1 -1
  104. package/lib/src/embed/bodyless-conversation.spec.js +9 -191
  105. package/lib/src/embed/bodyless-conversation.spec.js.map +1 -1
  106. package/lib/src/embed/conversation.spec.js +30 -2
  107. package/lib/src/embed/conversation.spec.js.map +1 -1
  108. package/lib/src/embed/embedConfig.d.ts +9 -7
  109. package/lib/src/embed/embedConfig.d.ts.map +1 -1
  110. package/lib/src/embed/embedConfig.js +9 -7
  111. package/lib/src/embed/embedConfig.js.map +1 -1
  112. package/lib/src/embed/liveboard.d.ts +56 -17
  113. package/lib/src/embed/liveboard.d.ts.map +1 -1
  114. package/lib/src/embed/liveboard.js +49 -5
  115. package/lib/src/embed/liveboard.js.map +1 -1
  116. package/lib/src/embed/liveboard.spec.js +218 -11
  117. package/lib/src/embed/liveboard.spec.js.map +1 -1
  118. package/lib/src/errors.d.ts +1 -0
  119. package/lib/src/errors.d.ts.map +1 -1
  120. package/lib/src/errors.js +1 -0
  121. package/lib/src/errors.js.map +1 -1
  122. package/lib/src/index.d.ts +2 -2
  123. package/lib/src/index.d.ts.map +1 -1
  124. package/lib/src/index.js +2 -2
  125. package/lib/src/index.js.map +1 -1
  126. package/lib/src/react/all-types-export.d.ts +1 -1
  127. package/lib/src/react/all-types-export.d.ts.map +1 -1
  128. package/lib/src/react/all-types-export.js +1 -1
  129. package/lib/src/react/all-types-export.js.map +1 -1
  130. package/lib/src/react/index.d.ts +73 -20
  131. package/lib/src/react/index.d.ts.map +1 -1
  132. package/lib/src/react/index.js +79 -43
  133. package/lib/src/react/index.js.map +1 -1
  134. package/lib/src/react/index.spec.js +441 -103
  135. package/lib/src/react/index.spec.js.map +1 -1
  136. package/lib/src/types.d.ts +331 -13
  137. package/lib/src/types.d.ts.map +1 -1
  138. package/lib/src/types.js +296 -8
  139. package/lib/src/types.js.map +1 -1
  140. package/lib/src/utils/global-styles.js +1 -1
  141. package/lib/src/utils/graphql/nlsService/conversation-service.d.ts.map +1 -1
  142. package/lib/src/utils/graphql/nlsService/conversation-service.js +7 -1
  143. package/lib/src/utils/graphql/nlsService/conversation-service.js.map +1 -1
  144. package/lib/src/utils/processTrigger.js +2 -1
  145. package/lib/src/utils/processTrigger.js.map +1 -1
  146. package/lib/src/utils.d.ts +6 -0
  147. package/lib/src/utils.d.ts.map +1 -1
  148. package/lib/src/utils.js +21 -2
  149. package/lib/src/utils.js.map +1 -1
  150. package/lib/src/utils.spec.js +238 -2
  151. package/lib/src/utils.spec.js.map +1 -1
  152. package/lib/src/visual-embed-sdk.d.ts +474 -61
  153. package/package.json +1 -1
  154. package/src/config.spec.ts +11 -0
  155. package/src/embed/app.spec.ts +486 -30
  156. package/src/embed/app.ts +133 -27
  157. package/src/embed/bodyless-conversation.spec.ts +9 -203
  158. package/src/embed/bodyless-conversation.ts +34 -10
  159. package/src/embed/conversation.spec.ts +40 -2
  160. package/src/embed/embedConfig.ts +10 -8
  161. package/src/embed/liveboard.spec.ts +259 -5
  162. package/src/embed/liveboard.ts +98 -27
  163. package/src/errors.ts +1 -0
  164. package/src/index.ts +2 -0
  165. package/src/react/all-types-export.ts +2 -1
  166. package/src/react/index.spec.tsx +558 -157
  167. package/src/react/index.tsx +117 -51
  168. package/src/types.ts +368 -50
  169. package/src/utils/global-styles.ts +1 -1
  170. package/src/utils/graphql/nlsService/conversation-service.ts +7 -1
  171. package/src/utils/processTrigger.ts +1 -1
  172. package/src/utils.spec.ts +279 -2
  173. package/src/utils.ts +28 -2
@@ -3,8 +3,8 @@ import {
3
3
  SpotterEmbedViewConfig,
4
4
  } from './conversation';
5
5
  import * as authInstance from '../auth';
6
- import { init } from '../index';
7
- import { AuthType } from '../types';
6
+ import { Action, init } from '../index';
7
+ import { AuthType, Param } from '../types';
8
8
  import {
9
9
  getDocumentBody,
10
10
  getIFrameSrc,
@@ -142,4 +142,42 @@ describe('ConversationEmbed', () => {
142
142
  `http://${thoughtSpotHost}/v2/?${defaultParams}&isSpotterExperienceEnabled=true&enableDataPanelV2=true#/embed/insights/conv-assist?worksheet=worksheetId&query=searchQuery`,
143
143
  );
144
144
  });
145
+
146
+ it('should render the conversation embed with hidden actions of InConversationTraining if set', async () => {
147
+ const viewConfig: SpotterEmbedViewConfig = {
148
+ worksheetId: 'worksheetId',
149
+ searchOptions: {
150
+ searchQuery: 'searchQuery',
151
+ },
152
+ dataPanelV2: true,
153
+ hiddenActions: [Action.InConversationTraining]
154
+ };
155
+
156
+ const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
157
+ await conversationEmbed.render();
158
+ expectUrlMatchesWithParams(
159
+ getIFrameSrc(),
160
+ `http://${thoughtSpotHost}/v2/?${defaultParams}&hideAction=[%22${Action.ReportError}%22,%22${Action.InConversationTraining}%22]&isSpotterExperienceEnabled=true&enableDataPanelV2=true#/embed/insights/conv-assist?worksheet=worksheetId&query=searchQuery`,
161
+ );
162
+ });
163
+
164
+ it('should render the conversation embed with disabled actions of InConversationTraining if set', async () => {
165
+ const disabledReason = 'testing disabled reason';
166
+ const viewConfig: SpotterEmbedViewConfig = {
167
+ worksheetId: 'worksheetId',
168
+ searchOptions: {
169
+ searchQuery: 'searchQuery',
170
+ },
171
+ dataPanelV2: true,
172
+ disabledActions: [Action.InConversationTraining],
173
+ disabledActionReason: disabledReason,
174
+ };
175
+
176
+ const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
177
+ await conversationEmbed.render();
178
+ expectUrlMatchesWithParams(
179
+ getIFrameSrc(),
180
+ `http://${thoughtSpotHost}/v2/?${defaultParams}&${Param.DisableActions}=[%22${Action.InConversationTraining}%22]&${Param.DisableActionReason}=${disabledReason}&isSpotterExperienceEnabled=true&enableDataPanelV2=true#/embed/insights/conv-assist?worksheet=worksheetId&query=searchQuery`,
181
+ );
182
+ });
145
183
  });
@@ -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,259 @@ 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
+ // Wait for the post-render events to be registered
1000
+ await executeAfterWait(() => {
1001
+ expect(addEventListenerSpy).toHaveBeenCalledWith('resize', expect.anything());
1002
+ expect(addEventListenerSpy).toHaveBeenCalledWith('scroll', expect.anything(), true);
1003
+ }, 100);
1004
+
1005
+ addEventListenerSpy.mockRestore();
1006
+ });
1007
+
1008
+ test('should remove window event listeners on destroy when fullHeight and lazyLoadingForFullHeight are enabled', async () => {
1009
+ const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener');
1010
+
1011
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1012
+ ...defaultViewConfig,
1013
+ liveboardId,
1014
+ fullHeight: true,
1015
+ lazyLoadingForFullHeight: true,
1016
+ } as LiveboardViewConfig);
1017
+
1018
+ await liveboardEmbed.render();
1019
+ liveboardEmbed.destroy();
1020
+
1021
+ expect(removeEventListenerSpy).toHaveBeenCalledWith('resize', expect.anything());
1022
+ expect(removeEventListenerSpy).toHaveBeenCalledWith('scroll', expect.anything());
1023
+
1024
+ removeEventListenerSpy.mockRestore();
1025
+ });
1026
+
1027
+ test('should handle RequestVisibleEmbedCoordinates event and respond with correct data', async () => {
1028
+ // Mock the iframe element
1029
+ mockIFrame.getBoundingClientRect = jest.fn().mockReturnValue({
1030
+ top: 100,
1031
+ left: 150,
1032
+ bottom: 600,
1033
+ right: 800,
1034
+ width: 650,
1035
+ height: 500,
1036
+ });
1037
+ Object.defineProperty(mockIFrame, 'scrollHeight', { value: 500 });
1038
+
1039
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1040
+ ...defaultViewConfig,
1041
+ liveboardId,
1042
+ fullHeight: true,
1043
+ lazyLoadingForFullHeight: true,
1044
+ } as LiveboardViewConfig);
1045
+
1046
+ // Set the iframe before render
1047
+ (liveboardEmbed as any).iFrame = mockIFrame;
1048
+
1049
+ await liveboardEmbed.render();
1050
+
1051
+ // Create a mock responder function
1052
+ const mockResponder = jest.fn();
1053
+
1054
+ // Trigger the handler directly
1055
+ (liveboardEmbed as any).requestVisibleEmbedCoordinatesHandler({}, mockResponder);
1056
+
1057
+ // Verify the responder was called with the correct data
1058
+ expect(mockResponder).toHaveBeenCalledWith({
1059
+ type: EmbedEvent.RequestVisibleEmbedCoordinates,
1060
+ data: {
1061
+ top: 0,
1062
+ height: 500,
1063
+ left: 0,
1064
+ width: 650,
1065
+ },
1066
+ });
1067
+ });
1068
+ });
1069
+
816
1070
  describe('Host events for liveborad', () => {
817
1071
  test('Host event with empty param', async () => {
818
1072
  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,46 @@ 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.40.0 | ThoughtSpot:10.12.0.cl
342
+ */
343
+ lazyLoadingForFullHeight?: boolean;
344
+ /**
345
+ * The margin to be used for lazy loading.
346
+ *
347
+ * For example, if the margin is set to '10px',
348
+ * the visualization will be loaded 10px before the its top edge is visible in the
349
+ * viewport.
350
+ *
351
+ * The format is similar to CSS margin.
352
+ *
353
+ * @example
354
+ * ```js
355
+ * const embed = new LiveboardEmbed('#embed-container', {
356
+ * // ...other options
357
+ * fullHeight: true,
358
+ * lazyLoadingForFullHeight: true,
359
+ * // Using 0px, the visualization will be only loaded when its visible in the viewport.
360
+ * lazyLoadingMargin: '0px',
361
+ * })
362
+ * ```
363
+ * @type {string}
364
+ * @version SDK: 1.40.0 | ThoughtSpot:10.12.0.cl
365
+ */
366
+ lazyLoadingMargin?: string;
345
367
  }
346
368
 
347
369
  /**
@@ -364,19 +386,20 @@ export class LiveboardEmbed extends V1Embed {
364
386
 
365
387
  private defaultHeight = 500;
366
388
 
367
-
389
+
368
390
  constructor(domSelector: DOMSelector, viewConfig: LiveboardViewConfig) {
369
391
  viewConfig.embedComponentType = 'LiveboardEmbed';
370
392
  super(domSelector, viewConfig);
371
393
  if (this.viewConfig.fullHeight === true) {
372
394
  if (this.viewConfig.vizId) {
373
395
  logger.warn('Full height is currently only supported for Liveboard embeds.' +
374
- 'Using full height with vizId might lead to unexpected behavior.');
396
+ 'Using full height with vizId might lead to unexpected behavior.');
375
397
  }
376
398
 
377
399
  this.on(EmbedEvent.RouteChange, this.setIframeHeightForNonEmbedLiveboard);
378
400
  this.on(EmbedEvent.EmbedHeight, this.updateIFrameHeight);
379
401
  this.on(EmbedEvent.EmbedIframeCenter, this.embedIframeCenter);
402
+ this.on(EmbedEvent.RequestVisibleEmbedCoordinates, this.requestVisibleEmbedCoordinatesHandler);
380
403
  }
381
404
  }
382
405
 
@@ -385,7 +408,7 @@ export class LiveboardEmbed extends V1Embed {
385
408
  * embedded Liveboard or visualization.
386
409
  */
387
410
  protected getEmbedParams() {
388
- let params = {};
411
+ let params: any = {};
389
412
  params = this.getBaseQueryParams(params);
390
413
  const {
391
414
  enableVizTransformations,
@@ -411,7 +434,7 @@ export class LiveboardEmbed extends V1Embed {
411
434
  oAuthPollingInterval,
412
435
  isForceRedirect,
413
436
  dataSourceId,
414
- coverAndFilterOptionInPDF,
437
+ coverAndFilterOptionInPDF = false,
415
438
  isLiveboardStylingAndGroupingEnabled,
416
439
  } = this.viewConfig;
417
440
 
@@ -420,6 +443,10 @@ export class LiveboardEmbed extends V1Embed {
420
443
 
421
444
  if (fullHeight === true) {
422
445
  params[Param.fullHeight] = true;
446
+ if (this.viewConfig.lazyLoadingForFullHeight) {
447
+ params[Param.IsLazyLoadingForEmbedEnabled] = true;
448
+ params[Param.RootMarginForLazyLoad] = this.viewConfig.lazyLoadingMargin;
449
+ }
423
450
  }
424
451
  if (defaultHeight) {
425
452
  this.defaultHeight = defaultHeight;
@@ -471,9 +498,6 @@ export class LiveboardEmbed extends V1Embed {
471
498
  params[Param.DataSourceId] = dataSourceId;
472
499
  }
473
500
 
474
- if (coverAndFilterOptionInPDF !== undefined) {
475
- params[Param.CoverAndFilterOptionInPDF] = coverAndFilterOptionInPDF;
476
- }
477
501
 
478
502
  if (isLiveboardStylingAndGroupingEnabled !== undefined) {
479
503
  params[Param.IsLiveboardStylingAndGroupingEnabled] = isLiveboardStylingAndGroupingEnabled;
@@ -486,6 +510,7 @@ export class LiveboardEmbed extends V1Embed {
486
510
  params[Param.HideIrrelevantFiltersInTab] = hideIrrelevantChipsInLiveboardTabs;
487
511
  params[Param.DataPanelV2Enabled] = dataPanelV2;
488
512
  params[Param.EnableCustomColumnGroups] = enableCustomColumnGroups;
513
+ params[Param.CoverAndFilterOptionInPDF] = coverAndFilterOptionInPDF;
489
514
  const queryParams = getQueryParamString(params, true);
490
515
 
491
516
  return queryParams;
@@ -504,6 +529,23 @@ export class LiveboardEmbed extends V1Embed {
504
529
  return suffix;
505
530
  }
506
531
 
532
+ private sendFullHeightLazyLoadData = () => {
533
+ const data = calculateVisibleElementData(this.iFrame);
534
+ this.trigger(HostEvent.VisibleEmbedCoordinates, data);
535
+ }
536
+
537
+ /**
538
+ * This is a handler for the RequestVisibleEmbedCoordinates event.
539
+ * It is used to send the visible coordinates data to the host application.
540
+ * @param data The event payload
541
+ * @param responder The responder function
542
+ */
543
+ private requestVisibleEmbedCoordinatesHandler = (data: MessagePayload, responder: any) => {
544
+ logger.info('Sending RequestVisibleEmbedCoordinates', data);
545
+ const visibleCoordinatesData = calculateVisibleElementData(this.iFrame);
546
+ responder({ type: EmbedEvent.RequestVisibleEmbedCoordinates, data: visibleCoordinatesData });
547
+ }
548
+
507
549
  /**
508
550
  * Construct the URL of the embedded ThoughtSpot Liveboard or visualization
509
551
  * to be loaded within the iFrame.
@@ -529,6 +571,7 @@ export class LiveboardEmbed extends V1Embed {
529
571
  */
530
572
  private updateIFrameHeight = (data: MessagePayload) => {
531
573
  this.setIFrameHeight(Math.max(data.data, this.defaultHeight));
574
+ this.sendFullHeightLazyLoadData();
532
575
  };
533
576
 
534
577
  private embedIframeCenter = (data: MessagePayload, responder: any) => {
@@ -602,7 +645,7 @@ export class LiveboardEmbed extends V1Embed {
602
645
  }
603
646
 
604
647
  protected beforePrerenderVisible(): void {
605
- const embedObj = this.insertedDomEl?.[this.embedNodeKey] as LiveboardEmbed;
648
+ const embedObj = (this.insertedDomEl as any)?.[this.embedNodeKey] as LiveboardEmbed;
606
649
 
607
650
  if (isUndefined(embedObj)) return;
608
651
 
@@ -642,6 +685,33 @@ export class LiveboardEmbed extends V1Embed {
642
685
  }
643
686
  return super.trigger(messageType, dataWithVizId);
644
687
  }
688
+ /**
689
+ * Destroys the ThoughtSpot embed, and remove any nodes from the DOM.
690
+ * @version SDK: 1.39.0 | ThoughtSpot: 10.10.0.cl
691
+ */
692
+ public destroy() {
693
+ super.destroy();
694
+ this.unregisterLazyLoadEvents();
695
+ }
696
+
697
+ private postRender() {
698
+ this.registerLazyLoadEvents();
699
+ }
700
+
701
+ private registerLazyLoadEvents() {
702
+ if (this.viewConfig.fullHeight && this.viewConfig.lazyLoadingForFullHeight) {
703
+ // TODO: Use passive: true, install modernizr to check for passive
704
+ window.addEventListener('resize', this.sendFullHeightLazyLoadData);
705
+ window.addEventListener('scroll', this.sendFullHeightLazyLoadData, true);
706
+ }
707
+ }
708
+
709
+ private unregisterLazyLoadEvents() {
710
+ if (this.viewConfig.fullHeight && this.viewConfig.lazyLoadingForFullHeight) {
711
+ window.removeEventListener('resize', this.sendFullHeightLazyLoadData);
712
+ window.removeEventListener('scroll', this.sendFullHeightLazyLoadData);
713
+ }
714
+ }
645
715
 
646
716
  /**
647
717
  * Render an embedded ThoughtSpot Liveboard or visualization
@@ -655,6 +725,7 @@ export class LiveboardEmbed extends V1Embed {
655
725
  await this.renderV1Embed(src);
656
726
  this.showPreviewLoader();
657
727
 
728
+ this.postRender();
658
729
  return this;
659
730
  }
660
731
 
package/src/errors.ts CHANGED
@@ -17,4 +17,5 @@ export const ERROR_MESSAGE = {
17
17
  CSP_FRAME_HOST_VIOLATION_LOG_MESSAGE: 'Please set up CSP correctly for the application to start working. For more information, see https://developers.thoughtspot.com/docs/security-settings#csp-viz-embed-hosts. \n If the issue persists, refer to https://developers.thoughtspot.com/docs/security-settings#csp-viz-embed-hosts',
18
18
  MISSING_REPORTING_OBSERVER: 'ReportingObserver not supported',
19
19
  RENDER_CALLED_BEFORE_INIT: 'Looks like render was called before calling init, the render won\'t start until init is called.\nFor more info check\n1. https://developers.thoughtspot.com/docs/Function_init#_init\n2.https://developers.thoughtspot.com/docs/getting-started#initSdk',
20
+ SPOTTER_AGENT_NOT_INITIALIZED: 'SpotterAgent not initialized',
20
21
  };
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  HomePageSearchBarMode,
16
16
  PrimaryNavbarVersion,
17
17
  HomePage,
18
+ ListPage,
18
19
  DataPanelCustomColumnGroupsAccordionState,
19
20
  } from './embed/app';
20
21
  import {
@@ -141,6 +142,7 @@ export {
141
142
  HomePageSearchBarMode,
142
143
  PrimaryNavbarVersion,
143
144
  HomePage,
145
+ ListPage,
144
146
  VizPoint,
145
147
  CustomActionPayload,
146
148
  UIPassthroughEvent,
@@ -12,7 +12,8 @@ export {
12
12
  SpotterEmbed,
13
13
  ConversationEmbed,
14
14
  PreRenderedConversationEmbed,
15
- SpotterAgentEmbed,
15
+ SpotterMessage,
16
+ useSpotterAgent,
16
17
  useEmbedRef,
17
18
  useInit,
18
19
  } from './index';