@thoughtspot/visual-embed-sdk 1.50.1 → 1.51.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 (156) hide show
  1. package/cjs/package.json +1 -1
  2. package/cjs/src/css-variables.d.ts +36 -0
  3. package/cjs/src/css-variables.d.ts.map +1 -1
  4. package/cjs/src/embed/app.d.ts +106 -14
  5. package/cjs/src/embed/app.d.ts.map +1 -1
  6. package/cjs/src/embed/app.js +47 -36
  7. package/cjs/src/embed/app.js.map +1 -1
  8. package/cjs/src/embed/app.spec.js +318 -149
  9. package/cjs/src/embed/app.spec.js.map +1 -1
  10. package/cjs/src/embed/conversation.d.ts +204 -1
  11. package/cjs/src/embed/conversation.d.ts.map +1 -1
  12. package/cjs/src/embed/conversation.js +19 -15
  13. package/cjs/src/embed/conversation.js.map +1 -1
  14. package/cjs/src/embed/conversation.spec.js +198 -17
  15. package/cjs/src/embed/conversation.spec.js.map +1 -1
  16. package/cjs/src/embed/events.spec.js +42 -0
  17. package/cjs/src/embed/events.spec.js.map +1 -1
  18. package/cjs/src/embed/host-events.spec.js +45 -0
  19. package/cjs/src/embed/host-events.spec.js.map +1 -1
  20. package/cjs/src/embed/hostEventClient/contracts.d.ts +15 -0
  21. package/cjs/src/embed/hostEventClient/contracts.d.ts.map +1 -1
  22. package/cjs/src/embed/hostEventClient/contracts.js +1 -0
  23. package/cjs/src/embed/hostEventClient/contracts.js.map +1 -1
  24. package/cjs/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  25. package/cjs/src/embed/hostEventClient/host-event-client.js +1 -0
  26. package/cjs/src/embed/hostEventClient/host-event-client.js.map +1 -1
  27. package/cjs/src/embed/hostEventClient/host-event-client.spec.js +9 -0
  28. package/cjs/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
  29. package/cjs/src/embed/liveboard.d.ts +36 -1
  30. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  31. package/cjs/src/embed/liveboard.js +26 -6
  32. package/cjs/src/embed/liveboard.js.map +1 -1
  33. package/cjs/src/embed/liveboard.spec.js +83 -2
  34. package/cjs/src/embed/liveboard.spec.js.map +1 -1
  35. package/cjs/src/embed/spotter-utils.d.ts +8 -1
  36. package/cjs/src/embed/spotter-utils.d.ts.map +1 -1
  37. package/cjs/src/embed/spotter-utils.js +14 -1
  38. package/cjs/src/embed/spotter-utils.js.map +1 -1
  39. package/cjs/src/embed/spotter-utils.spec.js +61 -0
  40. package/cjs/src/embed/spotter-utils.spec.js.map +1 -1
  41. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  42. package/cjs/src/embed/ts-embed.js +15 -0
  43. package/cjs/src/embed/ts-embed.js.map +1 -1
  44. package/cjs/src/index.d.ts +2 -2
  45. package/cjs/src/index.d.ts.map +1 -1
  46. package/cjs/src/index.js.map +1 -1
  47. package/cjs/src/mixpanel-service.d.ts +4 -0
  48. package/cjs/src/mixpanel-service.d.ts.map +1 -1
  49. package/cjs/src/mixpanel-service.js +4 -0
  50. package/cjs/src/mixpanel-service.js.map +1 -1
  51. package/cjs/src/types.d.ts +460 -15
  52. package/cjs/src/types.d.ts.map +1 -1
  53. package/cjs/src/types.js +428 -8
  54. package/cjs/src/types.js.map +1 -1
  55. package/dist/{index-fCne7kmU.js → index-aBzH5ZwF.js} +1 -1
  56. package/dist/src/css-variables.d.ts +36 -0
  57. package/dist/src/css-variables.d.ts.map +1 -1
  58. package/dist/src/embed/app.d.ts +106 -14
  59. package/dist/src/embed/app.d.ts.map +1 -1
  60. package/dist/src/embed/conversation.d.ts +204 -1
  61. package/dist/src/embed/conversation.d.ts.map +1 -1
  62. package/dist/src/embed/hostEventClient/contracts.d.ts +15 -0
  63. package/dist/src/embed/hostEventClient/contracts.d.ts.map +1 -1
  64. package/dist/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  65. package/dist/src/embed/liveboard.d.ts +36 -1
  66. package/dist/src/embed/liveboard.d.ts.map +1 -1
  67. package/dist/src/embed/spotter-utils.d.ts +8 -1
  68. package/dist/src/embed/spotter-utils.d.ts.map +1 -1
  69. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  70. package/dist/src/index.d.ts +2 -2
  71. package/dist/src/index.d.ts.map +1 -1
  72. package/dist/src/mixpanel-service.d.ts +4 -0
  73. package/dist/src/mixpanel-service.d.ts.map +1 -1
  74. package/dist/src/types.d.ts +460 -15
  75. package/dist/src/types.d.ts.map +1 -1
  76. package/dist/tsembed-react.es.js +558 -69
  77. package/dist/tsembed-react.js +557 -68
  78. package/dist/tsembed.es.js +556 -68
  79. package/dist/tsembed.js +555 -67
  80. package/dist/visual-embed-sdk-react-full.d.ts +845 -27
  81. package/dist/visual-embed-sdk-react.d.ts +851 -29
  82. package/dist/visual-embed-sdk.d.ts +855 -29
  83. package/lib/package.json +1 -1
  84. package/lib/src/css-variables.d.ts +36 -0
  85. package/lib/src/css-variables.d.ts.map +1 -1
  86. package/lib/src/embed/app.d.ts +106 -14
  87. package/lib/src/embed/app.d.ts.map +1 -1
  88. package/lib/src/embed/app.js +48 -37
  89. package/lib/src/embed/app.js.map +1 -1
  90. package/lib/src/embed/app.spec.js +319 -150
  91. package/lib/src/embed/app.spec.js.map +1 -1
  92. package/lib/src/embed/conversation.d.ts +204 -1
  93. package/lib/src/embed/conversation.d.ts.map +1 -1
  94. package/lib/src/embed/conversation.js +21 -17
  95. package/lib/src/embed/conversation.js.map +1 -1
  96. package/lib/src/embed/conversation.spec.js +198 -17
  97. package/lib/src/embed/conversation.spec.js.map +1 -1
  98. package/lib/src/embed/events.spec.js +42 -0
  99. package/lib/src/embed/events.spec.js.map +1 -1
  100. package/lib/src/embed/host-events.spec.js +45 -0
  101. package/lib/src/embed/host-events.spec.js.map +1 -1
  102. package/lib/src/embed/hostEventClient/contracts.d.ts +15 -0
  103. package/lib/src/embed/hostEventClient/contracts.d.ts.map +1 -1
  104. package/lib/src/embed/hostEventClient/contracts.js +1 -0
  105. package/lib/src/embed/hostEventClient/contracts.js.map +1 -1
  106. package/lib/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  107. package/lib/src/embed/hostEventClient/host-event-client.js +1 -0
  108. package/lib/src/embed/hostEventClient/host-event-client.js.map +1 -1
  109. package/lib/src/embed/hostEventClient/host-event-client.spec.js +9 -0
  110. package/lib/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
  111. package/lib/src/embed/liveboard.d.ts +36 -1
  112. package/lib/src/embed/liveboard.d.ts.map +1 -1
  113. package/lib/src/embed/liveboard.js +26 -6
  114. package/lib/src/embed/liveboard.js.map +1 -1
  115. package/lib/src/embed/liveboard.spec.js +83 -2
  116. package/lib/src/embed/liveboard.spec.js.map +1 -1
  117. package/lib/src/embed/spotter-utils.d.ts +8 -1
  118. package/lib/src/embed/spotter-utils.d.ts.map +1 -1
  119. package/lib/src/embed/spotter-utils.js +12 -0
  120. package/lib/src/embed/spotter-utils.js.map +1 -1
  121. package/lib/src/embed/spotter-utils.spec.js +62 -1
  122. package/lib/src/embed/spotter-utils.spec.js.map +1 -1
  123. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  124. package/lib/src/embed/ts-embed.js +15 -0
  125. package/lib/src/embed/ts-embed.js.map +1 -1
  126. package/lib/src/index.d.ts +2 -2
  127. package/lib/src/index.d.ts.map +1 -1
  128. package/lib/src/index.js.map +1 -1
  129. package/lib/src/mixpanel-service.d.ts +4 -0
  130. package/lib/src/mixpanel-service.d.ts.map +1 -1
  131. package/lib/src/mixpanel-service.js +4 -0
  132. package/lib/src/mixpanel-service.js.map +1 -1
  133. package/lib/src/types.d.ts +460 -15
  134. package/lib/src/types.d.ts.map +1 -1
  135. package/lib/src/types.js +428 -8
  136. package/lib/src/types.js.map +1 -1
  137. package/lib/src/visual-embed-sdk.d.ts +855 -29
  138. package/package.json +1 -1
  139. package/src/css-variables.ts +45 -0
  140. package/src/embed/app.spec.ts +390 -183
  141. package/src/embed/app.ts +172 -65
  142. package/src/embed/conversation.spec.ts +243 -20
  143. package/src/embed/conversation.ts +233 -17
  144. package/src/embed/events.spec.ts +52 -0
  145. package/src/embed/host-events.spec.ts +70 -0
  146. package/src/embed/hostEventClient/contracts.ts +16 -0
  147. package/src/embed/hostEventClient/host-event-client.spec.ts +12 -0
  148. package/src/embed/hostEventClient/host-event-client.ts +1 -0
  149. package/src/embed/liveboard.spec.ts +108 -2
  150. package/src/embed/liveboard.ts +76 -14
  151. package/src/embed/spotter-utils.spec.ts +68 -1
  152. package/src/embed/spotter-utils.ts +16 -1
  153. package/src/embed/ts-embed.ts +18 -0
  154. package/src/index.ts +2 -1
  155. package/src/mixpanel-service.ts +4 -0
  156. package/src/types.ts +462 -14
@@ -82,6 +82,46 @@ describe('test communication between host app and ThoughtSpot', () => {
82
82
  });
83
83
  });
84
84
 
85
+ test('should trigger PinSpotterConversation host event to ThoughtSpot app', async () => {
86
+ mockMessageChannel();
87
+ const appEmbed = new AppEmbed(getRootEl(), defaultViewConfig);
88
+ await appEmbed.render();
89
+ const iframe = getIFrameEl();
90
+ jest.spyOn(iframe.contentWindow, 'postMessage');
91
+ await executeAfterWait(() => {
92
+ appEmbed.trigger(HostEvent.PinSpotterConversation, {
93
+ conversationId: 'c1',
94
+ });
95
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
96
+ expect.objectContaining({
97
+ type: HostEvent.PinSpotterConversation,
98
+ }),
99
+ expect.anything(),
100
+ expect.anything(),
101
+ );
102
+ });
103
+ });
104
+
105
+ test('should trigger UnpinSpotterConversation host event to ThoughtSpot app', async () => {
106
+ mockMessageChannel();
107
+ const appEmbed = new AppEmbed(getRootEl(), defaultViewConfig);
108
+ await appEmbed.render();
109
+ const iframe = getIFrameEl();
110
+ jest.spyOn(iframe.contentWindow, 'postMessage');
111
+ await executeAfterWait(() => {
112
+ appEmbed.trigger(HostEvent.UnpinSpotterConversation, {
113
+ conversationId: 'c1',
114
+ });
115
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
116
+ expect.objectContaining({
117
+ type: HostEvent.UnpinSpotterConversation,
118
+ }),
119
+ expect.anything(),
120
+ expect.anything(),
121
+ );
122
+ });
123
+ });
124
+
85
125
  // TODO: enable test once we are actually able to load stuff in the iframe
86
126
  xtest('should trigger iframe load event', async () => {
87
127
  const onLoadSpy = jest.fn();
@@ -511,6 +551,8 @@ describe('EmbedEvent basic routing', () => {
511
551
  ['SpotterConversationRenamed', EmbedEvent.SpotterConversationRenamed],
512
552
  ['SpotterConversationDeleted', EmbedEvent.SpotterConversationDeleted],
513
553
  ['SpotterConversationSelected', EmbedEvent.SpotterConversationSelected],
554
+ ['SpotterConversationPinned', EmbedEvent.SpotterConversationPinned],
555
+ ['SpotterConversationUnpinned', EmbedEvent.SpotterConversationUnpinned],
514
556
  ['EmbedPageContextChanged', EmbedEvent.EmbedPageContextChanged],
515
557
  ['Subscribed', EmbedEvent.Subscribed],
516
558
  ['SendTestScheduleEmail', EmbedEvent.SendTestScheduleEmail],
@@ -523,6 +565,16 @@ describe('EmbedEvent basic routing', () => {
523
565
  ['SpotterVizClosed', EmbedEvent.SpotterVizClosed],
524
566
  ['RefreshLiveboardBrowserCache', EmbedEvent.RefreshLiveboardBrowserCache],
525
567
  ['V1Data', EmbedEvent.V1Data],
568
+ ['SpotterShareConversationButtonHeaderClicked', EmbedEvent.SpotterShareConversationButtonHeaderClicked],
569
+ ['SpotterShareConversationMenuItemSidebarClicked', EmbedEvent.SpotterShareConversationMenuItemSidebarClicked],
570
+ ['SpotterConversationShared', EmbedEvent.SpotterConversationShared],
571
+ ['SpotterConversationShareRevoked', EmbedEvent.SpotterConversationShareRevoked],
572
+ ['SpotterSharedConversationViewed', EmbedEvent.SpotterSharedConversationViewed],
573
+ ['SpotterShareIncludeNewMessagesCheckboxToggled', EmbedEvent.SpotterShareIncludeNewMessagesCheckboxToggled],
574
+ ['SpotterShareStaleInfoBannerDismissed', EmbedEvent.SpotterShareStaleInfoBannerDismissed],
575
+ ['SpotterShareModalConfirmButtonClicked', EmbedEvent.SpotterShareModalConfirmButtonClicked],
576
+ ['SpotterShareModalCancelButtonClicked', EmbedEvent.SpotterShareModalCancelButtonClicked],
577
+ ['SpotterSharedConversationExitButtonClicked', EmbedEvent.SpotterSharedConversationExitButtonClicked],
526
578
  ];
527
579
 
528
580
  test.each(UNCOVERED_EVENTS)(
@@ -149,6 +149,37 @@ describe('HostEvent.OpenFilter', () => {
149
149
  });
150
150
  });
151
151
 
152
+ // ---------------------------------------------------------------------------
153
+ // 10b – OpenParameter
154
+ // ---------------------------------------------------------------------------
155
+ describe('HostEvent.OpenParameter', () => {
156
+ test('postMessage type is openParameter (camelCase)', async () => {
157
+ mockMessageChannel();
158
+ const { lb, iframe } = await renderLiveboard();
159
+ await executeAfterWait(() => {
160
+ lb.trigger(HostEvent.OpenParameter, { parameter: { parameterId: 'p1' } } as any);
161
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
162
+ expect.objectContaining({ type: 'openParameter' }),
163
+ thoughtSpotHost,
164
+ expect.anything(),
165
+ );
166
+ });
167
+ });
168
+
169
+ test('accepts a payload with only parameterName', async () => {
170
+ mockMessageChannel();
171
+ const { lb, iframe } = await renderLiveboard();
172
+ await executeAfterWait(() => {
173
+ lb.trigger(HostEvent.OpenParameter, { parameter: { parameterName: 'Param 1' } } as any);
174
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
175
+ expect.objectContaining({ type: 'openParameter' }),
176
+ thoughtSpotHost,
177
+ expect.anything(),
178
+ );
179
+ });
180
+ });
181
+ });
182
+
152
183
  // ---------------------------------------------------------------------------
153
184
  // 11 – AddColumns
154
185
  // ---------------------------------------------------------------------------
@@ -1757,3 +1788,42 @@ describe('HostEvent.DownloadAsCsv — additional cases', () => {
1757
1788
  expect(result).toBeNull();
1758
1789
  });
1759
1790
  });
1791
+
1792
+ // ---------------------------------------------------------------------------
1793
+ // Spotter share-conversation HostEvents
1794
+ // ---------------------------------------------------------------------------
1795
+ describe('Spotter share-conversation HostEvents', () => {
1796
+ const shareEvents: Array<[string, HostEvent, string, any]> = [
1797
+ ['ShareSpotterConversation', HostEvent.ShareSpotterConversation, 'ShareSpotterConversation', { conversationId: 'abc' }],
1798
+ ['CloseSpotterShareConversation', HostEvent.CloseSpotterShareConversation, 'CloseSpotterShareConversation', {}],
1799
+ ['ExitSpotterSharedConversation', HostEvent.ExitSpotterSharedConversation, 'ExitSpotterSharedConversation', {}],
1800
+ ];
1801
+
1802
+ test.each(shareEvents)(
1803
+ '%s: postMessage type is the matching string value',
1804
+ async (name, event, typeString, payload) => {
1805
+ mockMessageChannel();
1806
+ const { lb, iframe } = await renderLiveboard();
1807
+ await executeAfterWait(() => {
1808
+ lb.trigger(event, payload as any);
1809
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
1810
+ expect.objectContaining({ type: typeString }),
1811
+ thoughtSpotHost,
1812
+ expect.anything(),
1813
+ );
1814
+ });
1815
+ },
1816
+ );
1817
+
1818
+ test.each(shareEvents)(
1819
+ '%s: returns null and calls handleError when !isRendered',
1820
+ async (name, event, typeString, payload) => {
1821
+ const lb = unrenderedLiveboard();
1822
+ const result = await lb.trigger(event, payload as any);
1823
+ expect(result).toBeNull();
1824
+ expect((lb as any).handleError).toHaveBeenCalledWith(
1825
+ expect.objectContaining({ code: EmbedErrorCodes.RENDER_NOT_CALLED }),
1826
+ );
1827
+ },
1828
+ );
1829
+ });
@@ -7,6 +7,12 @@ export interface LiveboardTab {
7
7
  [key: string]: any;
8
8
  }
9
9
 
10
+ export interface LiveboardGroup {
11
+ id: string;
12
+ name: string;
13
+ [key: string]: any;
14
+ }
15
+
10
16
  /**
11
17
  * Levels at which a filter or parameter can be applied.
12
18
  */
@@ -60,6 +66,7 @@ export enum UIPassthroughEvent {
60
66
  GetParameters = 'getParameters',
61
67
  GetTML = 'getTML',
62
68
  GetTabs = 'getTabs',
69
+ GetGroups = 'getGroups',
63
70
  GetExportRequestForCurrentPinboard = 'getExportRequestForCurrentPinboard',
64
71
  }
65
72
 
@@ -171,6 +178,14 @@ export type UIPassthroughContractBase = {
171
178
  Tabs: LiveboardTab[];
172
179
  };
173
180
  };
181
+ [UIPassthroughEvent.GetGroups]: {
182
+ request: Record<string, never>;
183
+ response: {
184
+ orderedGroupIds: string[];
185
+ numberOfGroups: number;
186
+ Groups: LiveboardGroup[];
187
+ };
188
+ };
174
189
  [UIPassthroughEvent.GetExportRequestForCurrentPinboard]: {
175
190
  request: Record<string, never>;
176
191
  response: {
@@ -225,6 +240,7 @@ export type EmbedApiHostEventMapping = {
225
240
  [HostEvent.GetParameters]: UIPassthroughEvent.GetParameters;
226
241
  [HostEvent.GetTML]: UIPassthroughEvent.GetTML;
227
242
  [HostEvent.GetTabs]: UIPassthroughEvent.GetTabs;
243
+ [HostEvent.GetGroups]: UIPassthroughEvent.GetGroups;
228
244
  [HostEvent.getExportRequestForCurrentPinboard]: UIPassthroughEvent.GetExportRequestForCurrentPinboard;
229
245
  }
230
246
 
@@ -375,6 +375,18 @@ describe('HostEventClient', () => {
375
375
  );
376
376
  });
377
377
 
378
+ it('should route GetGroups through passthrough and return data', async () => {
379
+ const { client } = createHostEventClient();
380
+ const mockResponse = [{ value: { orderedGroupIds: ['g1', 'g2'], numberOfGroups: 2, Groups: [] as any[] } }];
381
+ mockProcessTrigger
382
+ .mockResolvedValueOnce(mockGetAvailablePassthroughs())
383
+ .mockResolvedValueOnce(mockResponse);
384
+
385
+ const result = await client.triggerHostEvent(HostEvent.GetGroups, {});
386
+
387
+ expect(result).toEqual({ orderedGroupIds: ['g1', 'g2'], numberOfGroups: 2, Groups: [] });
388
+ });
389
+
378
390
  it('should throw when DrillDown payload has no valid points', async () => {
379
391
  const { client } = createHostEventClient();
380
392
  const invalidPayload = {} as any;
@@ -38,6 +38,7 @@ const PASSTHROUGH_MAP: Partial<Record<HostEvent, UIPassthroughEvent>> = {
38
38
  [HostEvent.GetParameters]: UIPassthroughEvent.GetParameters,
39
39
  [HostEvent.GetTML]: UIPassthroughEvent.GetTML,
40
40
  [HostEvent.GetTabs]: UIPassthroughEvent.GetTabs,
41
+ [HostEvent.GetGroups]: UIPassthroughEvent.GetGroups,
41
42
  [HostEvent.getExportRequestForCurrentPinboard]: UIPassthroughEvent.GetExportRequestForCurrentPinboard,
42
43
  };
43
44
 
@@ -275,7 +275,7 @@ describe('Liveboard/viz embed tests', () => {
275
275
  });
276
276
  });
277
277
 
278
- test('should disable isWYSIWYGLiveboardPDFEnabled by default in url', async () => {
278
+ test('should not set isWYSIWYGLiveboardPDFEnabled in url by default', async () => {
279
279
  const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
280
280
  ...defaultViewConfig,
281
281
  liveboardId,
@@ -284,7 +284,7 @@ describe('Liveboard/viz embed tests', () => {
284
284
  await executeAfterWait(() => {
285
285
  expectUrlMatchesWithParams(
286
286
  getIFrameSrc(),
287
- `http://${thoughtSpotHost}/?embedApp=true${defaultParams}&isWYSIWYGLiveboardPDFEnabled=false${prefixParams}#/embed/viz/${liveboardId}`,
287
+ `http://${thoughtSpotHost}/?embedApp=true${defaultParams}${prefixParams}#/embed/viz/${liveboardId}`,
288
288
  );
289
289
  });
290
290
  });
@@ -756,6 +756,47 @@ describe('Liveboard/viz embed tests', () => {
756
756
  });
757
757
  });
758
758
 
759
+ test('should add isScopedLiveboardFilteringEnabled flag and set value to true to the iframe src', async () => {
760
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
761
+ ...defaultViewConfig,
762
+ liveboardId,
763
+ isScopedLiveboardFilteringEnabled: true,
764
+ } as LiveboardViewConfig);
765
+ liveboardEmbed.render();
766
+ await executeAfterWait(() => {
767
+ expectUrlMatchesWithParams(
768
+ getIFrameSrc(),
769
+ `http://${thoughtSpotHost}/?embedApp=true${defaultParams}&isScopedLiveboardFilteringEnabled=true${prefixParams}#/embed/viz/${liveboardId}`,
770
+ );
771
+ });
772
+ });
773
+
774
+ test('should add isScopedLiveboardFilteringEnabled flag and set value to false to the iframe src', async () => {
775
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
776
+ ...defaultViewConfig,
777
+ liveboardId,
778
+ isScopedLiveboardFilteringEnabled: false,
779
+ } as LiveboardViewConfig);
780
+ liveboardEmbed.render();
781
+ await executeAfterWait(() => {
782
+ expectUrlMatchesWithParams(
783
+ getIFrameSrc(),
784
+ `http://${thoughtSpotHost}/?embedApp=true${defaultParams}&isScopedLiveboardFilteringEnabled=false${prefixParams}#/embed/viz/${liveboardId}`,
785
+ );
786
+ });
787
+ });
788
+
789
+ test('should not add isScopedLiveboardFilteringEnabled flag to the iframe src when undefined', async () => {
790
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
791
+ ...defaultViewConfig,
792
+ liveboardId,
793
+ } as LiveboardViewConfig);
794
+ liveboardEmbed.render();
795
+ await executeAfterWait(() => {
796
+ expect(getIFrameSrc()).not.toContain('isScopedLiveboardFilteringEnabled');
797
+ });
798
+ });
799
+
759
800
  test('should not append runtime filters in URL if excludeRuntimeFiltersfromURL is true', async () => {
760
801
  const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
761
802
  ...defaultViewConfig,
@@ -1339,6 +1380,35 @@ describe('Liveboard/viz embed tests', () => {
1339
1380
  });
1340
1381
  });
1341
1382
 
1383
+ test('should render the liveboard embed with showSpotterRadiance', async () => {
1384
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1385
+ ...defaultViewConfig,
1386
+ liveboardId,
1387
+ showSpotterRadiance: true,
1388
+ } as LiveboardViewConfig);
1389
+ await liveboardEmbed.render();
1390
+ await executeAfterWait(() => {
1391
+ expectUrlMatchesWithParams(
1392
+ getIFrameSrc(),
1393
+ `http://${thoughtSpotHost}/?embedApp=true${defaultParams}${prefixParams}&showSpotterRadiance=true#/embed/viz/${liveboardId}`,
1394
+ );
1395
+ });
1396
+ });
1397
+ test('should render the liveboard embed with showSpotterRadiance disabled', async () => {
1398
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1399
+ ...defaultViewConfig,
1400
+ liveboardId,
1401
+ showSpotterRadiance: false,
1402
+ } as LiveboardViewConfig);
1403
+ await liveboardEmbed.render();
1404
+ await executeAfterWait(() => {
1405
+ expectUrlMatchesWithParams(
1406
+ getIFrameSrc(),
1407
+ `http://${thoughtSpotHost}/?embedApp=true${defaultParams}${prefixParams}&showSpotterRadiance=false#/embed/viz/${liveboardId}`,
1408
+ );
1409
+ });
1410
+ });
1411
+
1342
1412
  test('should set hideToolResponseCardBranding parameter in url params via spotterChatConfig', async () => {
1343
1413
  const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
1344
1414
  ...defaultViewConfig,
@@ -2306,3 +2376,39 @@ describe('Liveboard/viz embed tests', () => {
2306
2376
  });
2307
2377
  });
2308
2378
  });
2379
+
2380
+ describe('LiveboardEmbed updatedSpotterExperience tests', () => {
2381
+ beforeEach(() => {
2382
+ document.body.innerHTML = getDocumentBody();
2383
+ });
2384
+
2385
+ test('should render the liveboard embed with updatedSpotterExperience', async () => {
2386
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
2387
+ ...defaultViewConfig,
2388
+ liveboardId,
2389
+ updatedSpotterExperience: true,
2390
+ } as LiveboardViewConfig);
2391
+ await liveboardEmbed.render();
2392
+ await executeAfterWait(() => {
2393
+ expectUrlMatchesWithParams(
2394
+ getIFrameSrc(),
2395
+ `http://${thoughtSpotHost}/?embedApp=true${defaultParams}${prefixParams}&updatedSpotterExperience=true#/embed/viz/${liveboardId}`,
2396
+ );
2397
+ });
2398
+ });
2399
+
2400
+ test('should render the liveboard embed with updatedSpotterExperience disabled', async () => {
2401
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
2402
+ ...defaultViewConfig,
2403
+ liveboardId,
2404
+ updatedSpotterExperience: false,
2405
+ } as LiveboardViewConfig);
2406
+ await liveboardEmbed.render();
2407
+ await executeAfterWait(() => {
2408
+ expectUrlMatchesWithParams(
2409
+ getIFrameSrc(),
2410
+ `http://${thoughtSpotHost}/?embedApp=true${defaultParams}${prefixParams}&updatedSpotterExperience=false#/embed/viz/${liveboardId}`,
2411
+ );
2412
+ });
2413
+ });
2414
+ });
@@ -402,11 +402,12 @@ export interface LiveboardViewConfig extends BaseViewConfig, LiveboardOtherViewC
402
402
  /**
403
403
  * Enables the 'what you see is what you get' PDF export for Liveboards. Each tab is rendered on a single page
404
404
  * following the exact UI layout, instead of splitting visualizations across multiple A4 pages.
405
- * This feature is GA from version 26.5.0.cl. It is disabled by default in embed deployments.
405
+ * This feature is GA from SDK version 1.51.0 and ThoughtSpot version 26.8.0.cl.
406
406
  *
407
407
  * Supported embed types: `AppEmbed`, `LiveboardEmbed`
408
408
  * @type {boolean}
409
409
  * @version SDK: 1.48.0 | ThoughtSpot: 26.5.0.cl
410
+ * @default true
410
411
  * @example
411
412
  * ```js
412
413
  * // Replace <EmbedComponent> with embed component name. For example, AppEmbed or LiveboardEmbed
@@ -419,10 +420,12 @@ export interface LiveboardViewConfig extends BaseViewConfig, LiveboardOtherViewC
419
420
  isContinuousLiveboardPDFEnabled?: boolean;
420
421
  /**
421
422
  * This flag is used to enable/disable the XLSX/CSV download option for Liveboards
423
+ * This feature is GA from SDK version 1.51.0 and ThoughtSpot version 26.6.0.cl.
422
424
  *
423
425
  * Supported embed types: `AppEmbed`, `LiveboardEmbed`
424
426
  * @type {boolean}
425
427
  * @version SDK: 1.46.0 | ThoughtSpot: 26.3.0.cl
428
+ * @default true
426
429
  * @example
427
430
  * ```js
428
431
  * // Replace <EmbedComponent> with embed component name. For example, AppEmbed or LiveboardEmbed
@@ -435,10 +438,12 @@ export interface LiveboardViewConfig extends BaseViewConfig, LiveboardOtherViewC
435
438
  isLiveboardXLSXCSVDownloadEnabled?: boolean;
436
439
  /**
437
440
  * This flag is used to enable/disable the granular XLSX/CSV schedules feature
441
+ * This feature is GA from SDK version 1.51.0 and ThoughtSpot version 26.6.0.cl.
438
442
  *
439
443
  * Supported embed types: `AppEmbed`, `LiveboardEmbed`
440
444
  * @type {boolean}
441
445
  * @version SDK: 1.46.0 | ThoughtSpot: 26.3.0.cl
446
+ * @default true
442
447
  * @example
443
448
  * ```js
444
449
  * // Replace <EmbedComponent> with embed component name. For example, AppEmbed or LiveboardEmbed
@@ -529,6 +534,21 @@ export interface LiveboardViewConfig extends BaseViewConfig, LiveboardOtherViewC
529
534
  * ```
530
535
  */
531
536
  updatedSpotterChatPrompt?: boolean;
537
+ /**
538
+ * showSpotterRadiance : Controls the radiance on the Spotter page.
539
+ *
540
+ * Supported embed types: `LiveboardEmbed`
541
+ * @version SDK: 1.52.0 | ThoughtSpot Cloud: 26.9.0.cl
542
+ * @default false
543
+ * @example
544
+ * ```js
545
+ * const embed = new LiveboardEmbed('#tsEmbed', {
546
+ * ... //other embed view config
547
+ * showSpotterRadiance : true,
548
+ * })
549
+ * ```
550
+ */
551
+ showSpotterRadiance?: boolean;
532
552
  /**
533
553
  * Enables the stop answer generation button in the Spotter embed UI,
534
554
  * allowing users to interrupt an ongoing answer generation.
@@ -604,6 +624,21 @@ export interface LiveboardViewConfig extends BaseViewConfig, LiveboardOtherViewC
604
624
  * ```
605
625
  */
606
626
  enableLiveboardDataCache?: boolean;
627
+ /**
628
+ * updatedSpotterExperience : Controls the updated Spotter experience.
629
+ *
630
+ * Supported embed types: `LiveboardEmbed`
631
+ * @version SDK: 1.52.0 | ThoughtSpot Cloud: 26.9.0.cl
632
+ * @default false
633
+ * @example
634
+ * ```js
635
+ * const embed = new LiveboardEmbed('#tsEmbed', {
636
+ * ... //other embed view config
637
+ * updatedSpotterExperience : true,
638
+ * })
639
+ * ```
640
+ */
641
+ updatedSpotterExperience?: boolean;
607
642
  }
608
643
 
609
644
  /**
@@ -679,34 +714,37 @@ export class LiveboardEmbed extends V1Embed {
679
714
  showLiveboardDescription,
680
715
  showLiveboardTitle,
681
716
  isLiveboardHeaderSticky = true,
682
- isLiveboardCompactHeaderEnabled = false,
717
+ isLiveboardCompactHeaderEnabled,
683
718
  showLiveboardVerifiedBadge = true,
684
719
  showLiveboardReverifyBanner = true,
685
- hideIrrelevantChipsInLiveboardTabs = false,
720
+ hideIrrelevantChipsInLiveboardTabs,
686
721
  showMaskedFilterChip = false,
687
- isLiveboardMasterpiecesEnabled = false,
722
+ isLiveboardMasterpiecesEnabled,
688
723
  newChartsLibrary,
689
- isEnhancedFilterInteractivityEnabled = false,
724
+ isEnhancedFilterInteractivityEnabled,
690
725
  enableAskSage,
691
726
  enable2ColumnLayout,
692
727
  dataPanelV2 = true,
728
+ updatedSpotterExperience,
693
729
  enableCustomColumnGroups = false,
694
730
  oAuthPollingInterval,
695
731
  isForceRedirect,
696
732
  dataSourceId,
697
- coverAndFilterOptionInPDF = false,
733
+ coverAndFilterOptionInPDF,
698
734
  isLiveboardStylingAndGroupingEnabled,
699
735
  isPNGInScheduledEmailsEnabled = false,
700
- isLiveboardXLSXCSVDownloadEnabled = false,
701
- isGranularXLSXCSVSchedulesEnabled = false,
736
+ isLiveboardXLSXCSVDownloadEnabled,
737
+ isGranularXLSXCSVSchedulesEnabled,
702
738
  showSpotterLimitations,
703
739
  isCentralizedLiveboardFilterUXEnabled = false,
740
+ isScopedLiveboardFilteringEnabled,
704
741
  isLinkParametersEnabled,
705
742
  updatedSpotterChatPrompt,
743
+ showSpotterRadiance,
706
744
  enableStopAnswerGenerationEmbed,
707
745
  spotterChatConfig,
708
746
  isThisPeriodInDateFiltersEnabled,
709
- isContinuousLiveboardPDFEnabled = false,
747
+ isContinuousLiveboardPDFEnabled,
710
748
  enableLiveboardDataCache,
711
749
  } = this.viewConfig;
712
750
 
@@ -732,6 +770,9 @@ export class LiveboardEmbed extends V1Embed {
732
770
  if (!isUndefined(updatedSpotterChatPrompt)) {
733
771
  params[Param.UpdatedSpotterChatPrompt] = !!updatedSpotterChatPrompt;
734
772
  }
773
+ if (!isUndefined(showSpotterRadiance)) {
774
+ params[Param.ShowSpotterRadiance] = !!showSpotterRadiance;
775
+ }
735
776
  if (!isUndefined(enableStopAnswerGenerationEmbed)) {
736
777
  params[Param.EnableStopAnswerGenerationEmbed] = !!enableStopAnswerGenerationEmbed;
737
778
  }
@@ -796,6 +837,10 @@ export class LiveboardEmbed extends V1Embed {
796
837
  params[Param.ShowSpotterLimitations] = showSpotterLimitations;
797
838
  }
798
839
 
840
+ if (!isUndefined(updatedSpotterExperience)) {
841
+ params[Param.UpdatedSpotterExperience] = !!updatedSpotterExperience;
842
+ }
843
+
799
844
  // Handle spotterChatConfig params
800
845
  if (spotterChatConfig) {
801
846
  const {
@@ -829,6 +874,12 @@ export class LiveboardEmbed extends V1Embed {
829
874
  ] = isCentralizedLiveboardFilterUXEnabled;
830
875
  }
831
876
 
877
+ if (isScopedLiveboardFilteringEnabled !== undefined) {
878
+ params[
879
+ Param.isScopedLiveboardFilteringEnabled
880
+ ] = isScopedLiveboardFilteringEnabled;
881
+ }
882
+
832
883
  if (isThisPeriodInDateFiltersEnabled !== undefined) {
833
884
  params[Param.IsThisPeriodInDateFiltersEnabled] = isThisPeriodInDateFiltersEnabled;
834
885
  }
@@ -846,16 +897,27 @@ export class LiveboardEmbed extends V1Embed {
846
897
  }
847
898
 
848
899
  params[Param.LiveboardHeaderSticky] = isLiveboardHeaderSticky;
849
- params[Param.LiveboardHeaderV2] = isLiveboardCompactHeaderEnabled;
900
+ if (!isUndefined(isLiveboardCompactHeaderEnabled)) {
901
+ params[Param.LiveboardHeaderV2] = isLiveboardCompactHeaderEnabled;
902
+ }
850
903
  params[Param.ShowLiveboardVerifiedBadge] = showLiveboardVerifiedBadge;
851
904
  params[Param.ShowLiveboardReverifyBanner] = showLiveboardReverifyBanner;
852
- params[Param.HideIrrelevantFiltersInTab] = hideIrrelevantChipsInLiveboardTabs;
905
+ if (!isUndefined(hideIrrelevantChipsInLiveboardTabs)) {
906
+ params[Param.HideIrrelevantFiltersInTab] = hideIrrelevantChipsInLiveboardTabs;
907
+ }
853
908
  params[Param.ShowMaskedFilterChip] = showMaskedFilterChip;
854
- params[Param.IsLiveboardMasterpiecesEnabled] = isLiveboardMasterpiecesEnabled;
855
- params[Param.IsEnhancedFilterInteractivityEnabled] = isEnhancedFilterInteractivityEnabled;
909
+ if (!isUndefined(isLiveboardMasterpiecesEnabled)) {
910
+ params[Param.IsLiveboardMasterpiecesEnabled] = isLiveboardMasterpiecesEnabled;
911
+ }
912
+ if (!isUndefined(isEnhancedFilterInteractivityEnabled)) {
913
+ params[Param.IsEnhancedFilterInteractivityEnabled] =
914
+ isEnhancedFilterInteractivityEnabled;
915
+ }
856
916
  params[Param.DataPanelV2Enabled] = dataPanelV2;
857
917
  params[Param.EnableCustomColumnGroups] = enableCustomColumnGroups;
858
- params[Param.CoverAndFilterOptionInPDF] = coverAndFilterOptionInPDF;
918
+ if (!isUndefined(coverAndFilterOptionInPDF)) {
919
+ params[Param.CoverAndFilterOptionInPDF] = coverAndFilterOptionInPDF;
920
+ }
859
921
 
860
922
  const queryParams = getQueryParamString(params, true);
861
923
 
@@ -1,4 +1,8 @@
1
- import { resolveEnablePastConversationsSidebar, buildSpotterSidebarAppInitData } from './spotter-utils';
1
+ import {
2
+ resolveEnablePastConversationsSidebar,
3
+ buildSpotterSidebarAppInitData,
4
+ buildSpotterShareConversationAppInitData,
5
+ } from './spotter-utils';
2
6
  import { ErrorDetailsTypes, EmbedErrorCodes } from '../types';
3
7
  import { ERROR_MESSAGE } from '../errors';
4
8
 
@@ -36,6 +40,22 @@ describe('buildSpotterSidebarAppInitData', () => {
36
40
  });
37
41
  });
38
42
 
43
+ it('forwards a nested spotterChatPinConfig object through untouched', () => {
44
+ const spotterChatPinConfig = {
45
+ enabled: true,
46
+ pinLabel: 'Pin to top',
47
+ unpinLabel: 'Unpin',
48
+ };
49
+ const result = buildSpotterSidebarAppInitData(base, {
50
+ spotterSidebarConfig: {
51
+ enablePastConversationsSidebar: true,
52
+ spotterChatPinConfig,
53
+ },
54
+ }, noopError);
55
+ expect(result.embedParams?.spotterSidebarConfig?.spotterChatPinConfig)
56
+ .toEqual(spotterChatPinConfig);
57
+ });
58
+
39
59
  it('promotes standalone flag into spotterSidebarConfig.enablePastConversationsSidebar', () => {
40
60
  const result = buildSpotterSidebarAppInitData(base, { enablePastConversationsSidebar: true }, noopError);
41
61
  expect(result.embedParams?.spotterSidebarConfig?.enablePastConversationsSidebar).toBe(true);
@@ -106,3 +126,50 @@ describe('buildSpotterSidebarAppInitData', () => {
106
126
  expect(result.embedParams?.spotterSidebarConfig?.enablePastConversationsSidebar).toBe(true);
107
127
  });
108
128
  });
129
+
130
+ describe('buildSpotterShareConversationAppInitData', () => {
131
+ const base = { type: 'APP_INIT' } as any;
132
+
133
+ it('returns base unchanged when no spotterShareConversationConfig', () => {
134
+ const result = buildSpotterShareConversationAppInitData(base, {});
135
+ expect(result).toBe(base);
136
+ });
137
+
138
+ it('nests spotterShareConversationConfig under embedParams', () => {
139
+ const result = buildSpotterShareConversationAppInitData(base, {
140
+ spotterShareConversationConfig: { enableShareConversation: true },
141
+ });
142
+ expect(result.embedParams?.spotterShareConversationConfig).toEqual({
143
+ enableShareConversation: true,
144
+ });
145
+ });
146
+
147
+ it('passes label override fields through untouched', () => {
148
+ const spotterShareConversationConfig = {
149
+ enableShareConversation: true,
150
+ spotterShareLabel: 'Share',
151
+ spotterShareModalTitle: 'Share conversation',
152
+ spotterShareConfirmLabel: 'Share',
153
+ spotterShareCancelLabel: 'Cancel',
154
+ spotterShareAddUsersLabel: 'Add users or groups',
155
+ spotterShareEmptyTitle: 'No users added yet',
156
+ spotterShareEmptySubtitle: 'Not shared with any user',
157
+ spotterShareIncludeNewMessagesLabel: 'Include new messages',
158
+ spotterShareUpToCurrentLabel: 'Share up to current moment',
159
+ spotterShareStaleInfoLabel: 'This snapshot may be stale',
160
+ };
161
+ const result = buildSpotterShareConversationAppInitData(base, {
162
+ spotterShareConversationConfig,
163
+ });
164
+ expect(result.embedParams?.spotterShareConversationConfig).toEqual(spotterShareConversationConfig);
165
+ });
166
+
167
+ it('preserves existing embedParams when nesting the share config', () => {
168
+ const withParams = { type: 'APP_INIT', embedParams: { existing: 'keep' } } as any;
169
+ const result = buildSpotterShareConversationAppInitData(withParams, {
170
+ spotterShareConversationConfig: { enableShareConversation: true },
171
+ });
172
+ expect(result.embedParams?.existing).toBe('keep');
173
+ expect(result.embedParams?.spotterShareConversationConfig?.enableShareConversation).toBe(true);
174
+ });
175
+ });
@@ -1,7 +1,7 @@
1
1
  import { DefaultAppInitData, ErrorDetailsTypes, EmbedErrorCodes } from '../types';
2
2
  import { validateHttpUrl } from '../utils';
3
3
  import { ERROR_MESSAGE } from '../errors';
4
- import type { SpotterSidebarViewConfig } from './conversation';
4
+ import type { SpotterSidebarViewConfig, SpotterShareConversationConfig } from './conversation';
5
5
  import type { VisualizationOverrides } from '../types';
6
6
 
7
7
  /**
@@ -79,3 +79,18 @@ export function buildSpotterSidebarAppInitData<T extends DefaultAppInitData>(
79
79
  },
80
80
  };
81
81
  }
82
+
83
+ export function buildSpotterShareConversationAppInitData<T extends DefaultAppInitData>(
84
+ initData: T,
85
+ viewConfig: { spotterShareConversationConfig?: SpotterShareConversationConfig },
86
+ ): T & { embedParams?: { spotterShareConversationConfig?: SpotterShareConversationConfig } } {
87
+ const { spotterShareConversationConfig } = viewConfig;
88
+ if (!spotterShareConversationConfig) return initData;
89
+ return {
90
+ ...initData,
91
+ embedParams: {
92
+ ...((initData as T & { embedParams?: Record<string, unknown> }).embedParams || {}),
93
+ spotterShareConversationConfig,
94
+ },
95
+ };
96
+ }