@thoughtspot/visual-embed-sdk 1.42.0 → 1.42.1-HE2

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 (101) hide show
  1. package/cjs/package.json +2 -2
  2. package/cjs/src/embed/app.d.ts +20 -0
  3. package/cjs/src/embed/app.d.ts.map +1 -1
  4. package/cjs/src/embed/app.js +7 -1
  5. package/cjs/src/embed/app.js.map +1 -1
  6. package/cjs/src/embed/app.spec.js +52 -0
  7. package/cjs/src/embed/app.spec.js.map +1 -1
  8. package/cjs/src/embed/hostEventClient/host-event-client.d.ts +3 -3
  9. package/cjs/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  10. package/cjs/src/embed/hostEventClient/host-event-client.js +6 -6
  11. package/cjs/src/embed/hostEventClient/host-event-client.js.map +1 -1
  12. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  13. package/cjs/src/embed/liveboard.js +4 -1
  14. package/cjs/src/embed/liveboard.js.map +1 -1
  15. package/cjs/src/embed/liveboard.spec.js +22 -0
  16. package/cjs/src/embed/liveboard.spec.js.map +1 -1
  17. package/cjs/src/embed/ts-embed.d.ts +14 -1
  18. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  19. package/cjs/src/embed/ts-embed.js +63 -23
  20. package/cjs/src/embed/ts-embed.js.map +1 -1
  21. package/cjs/src/embed/ts-embed.spec.js +83 -0
  22. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  23. package/cjs/src/errors.d.ts +1 -0
  24. package/cjs/src/errors.d.ts.map +1 -1
  25. package/cjs/src/errors.js +1 -0
  26. package/cjs/src/errors.js.map +1 -1
  27. package/cjs/src/types.d.ts +119 -61
  28. package/cjs/src/types.d.ts.map +1 -1
  29. package/cjs/src/types.js +103 -60
  30. package/cjs/src/types.js.map +1 -1
  31. package/cjs/src/utils/processTrigger.d.ts +2 -1
  32. package/cjs/src/utils/processTrigger.d.ts.map +1 -1
  33. package/cjs/src/utils/processTrigger.js +4 -2
  34. package/cjs/src/utils/processTrigger.js.map +1 -1
  35. package/dist/{index-BpSohedu.js → index-CWQnMX2L.js} +1 -1
  36. package/dist/index-Djtv-y7A.js +7371 -0
  37. package/dist/src/embed/app.d.ts +20 -0
  38. package/dist/src/embed/app.d.ts.map +1 -1
  39. package/dist/src/embed/hostEventClient/host-event-client.d.ts +3 -3
  40. package/dist/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  41. package/dist/src/embed/liveboard.d.ts.map +1 -1
  42. package/dist/src/embed/ts-embed.d.ts +14 -1
  43. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  44. package/dist/src/errors.d.ts +1 -0
  45. package/dist/src/errors.d.ts.map +1 -1
  46. package/dist/src/types.d.ts +119 -61
  47. package/dist/src/types.d.ts.map +1 -1
  48. package/dist/src/utils/processTrigger.d.ts +2 -1
  49. package/dist/src/utils/processTrigger.d.ts.map +1 -1
  50. package/dist/tsembed-react.es.js +191 -96
  51. package/dist/tsembed-react.js +190 -95
  52. package/dist/tsembed.es.js +191 -96
  53. package/dist/tsembed.js +190 -95
  54. package/dist/visual-embed-sdk-react-full.d.ts +153 -64
  55. package/dist/visual-embed-sdk-react.d.ts +153 -64
  56. package/dist/visual-embed-sdk.d.ts +153 -64
  57. package/lib/package.json +2 -2
  58. package/lib/src/embed/app.d.ts +20 -0
  59. package/lib/src/embed/app.d.ts.map +1 -1
  60. package/lib/src/embed/app.js +7 -1
  61. package/lib/src/embed/app.js.map +1 -1
  62. package/lib/src/embed/app.spec.js +52 -0
  63. package/lib/src/embed/app.spec.js.map +1 -1
  64. package/lib/src/embed/hostEventClient/host-event-client.d.ts +3 -3
  65. package/lib/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  66. package/lib/src/embed/hostEventClient/host-event-client.js +6 -6
  67. package/lib/src/embed/hostEventClient/host-event-client.js.map +1 -1
  68. package/lib/src/embed/liveboard.d.ts.map +1 -1
  69. package/lib/src/embed/liveboard.js +4 -1
  70. package/lib/src/embed/liveboard.js.map +1 -1
  71. package/lib/src/embed/liveboard.spec.js +22 -0
  72. package/lib/src/embed/liveboard.spec.js.map +1 -1
  73. package/lib/src/embed/ts-embed.d.ts +14 -1
  74. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  75. package/lib/src/embed/ts-embed.js +63 -23
  76. package/lib/src/embed/ts-embed.js.map +1 -1
  77. package/lib/src/embed/ts-embed.spec.js +83 -0
  78. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  79. package/lib/src/errors.d.ts +1 -0
  80. package/lib/src/errors.d.ts.map +1 -1
  81. package/lib/src/errors.js +1 -0
  82. package/lib/src/errors.js.map +1 -1
  83. package/lib/src/types.d.ts +119 -61
  84. package/lib/src/types.d.ts.map +1 -1
  85. package/lib/src/types.js +103 -60
  86. package/lib/src/types.js.map +1 -1
  87. package/lib/src/utils/processTrigger.d.ts +2 -1
  88. package/lib/src/utils/processTrigger.d.ts.map +1 -1
  89. package/lib/src/utils/processTrigger.js +4 -2
  90. package/lib/src/utils/processTrigger.js.map +1 -1
  91. package/package.json +2 -2
  92. package/src/embed/app.spec.ts +73 -0
  93. package/src/embed/app.ts +30 -0
  94. package/src/embed/hostEventClient/host-event-client.ts +7 -3
  95. package/src/embed/liveboard.spec.ts +30 -0
  96. package/src/embed/liveboard.ts +5 -0
  97. package/src/embed/ts-embed.spec.ts +111 -0
  98. package/src/embed/ts-embed.ts +76 -25
  99. package/src/errors.ts +1 -0
  100. package/src/types.ts +119 -61
  101. package/src/utils/processTrigger.ts +5 -2
@@ -23,7 +23,7 @@ export class HostEventClient {
23
23
  * @param {any} data Data to send with the host event
24
24
  * @returns {Promise<any>} - the response from the process trigger
25
25
  */
26
- protected async processTrigger(message: HostEvent, data: any): Promise<any> {
26
+ protected async processTrigger(message: HostEvent, data: any, context?: any): Promise<any> {
27
27
  if (!this.iFrame) {
28
28
  throw new Error('Iframe element is not set');
29
29
  }
@@ -34,6 +34,7 @@ export class HostEventClient {
34
34
  message,
35
35
  thoughtspotHost,
36
36
  data,
37
+ context,
37
38
  );
38
39
  }
39
40
 
@@ -65,8 +66,9 @@ export class HostEventClient {
65
66
  public async hostEventFallback(
66
67
  hostEvent: HostEvent,
67
68
  data: any,
69
+ context?: any,
68
70
  ): Promise<any> {
69
- return this.processTrigger(hostEvent, data);
71
+ return this.processTrigger(hostEvent, data, context);
70
72
  }
71
73
 
72
74
  /**
@@ -132,9 +134,11 @@ export class HostEventClient {
132
134
  public async triggerHostEvent<
133
135
  HostEventT extends HostEvent,
134
136
  PayloadT,
137
+ ContextT,
135
138
  >(
136
139
  hostEvent: HostEventT,
137
140
  payload?: TriggerPayload<PayloadT, HostEventT>,
141
+ context?: ContextT,
138
142
  ): Promise<TriggerResponse<PayloadT, HostEventT>> {
139
143
  switch (hostEvent) {
140
144
  case HostEvent.Pin:
@@ -144,7 +148,7 @@ export class HostEventClient {
144
148
  payload as HostEventRequest<HostEvent.SaveAnswer>,
145
149
  ) as any;
146
150
  default:
147
- return this.hostEventFallback(hostEvent, payload);
151
+ return this.hostEventFallback(hostEvent, payload, context as any);
148
152
  }
149
153
  }
150
154
  }
@@ -183,6 +183,36 @@ describe('Liveboard/viz embed tests', () => {
183
183
  });
184
184
  });
185
185
 
186
+ test('should set isLinkParametersEnabled to true in url', async () => {
187
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
188
+ isLinkParametersEnabled: true,
189
+ ...defaultViewConfig,
190
+ liveboardId,
191
+ } as LiveboardViewConfig);
192
+ liveboardEmbed.render();
193
+ await executeAfterWait(() => {
194
+ expectUrlMatchesWithParams(
195
+ getIFrameSrc(),
196
+ `http://${thoughtSpotHost}/?embedApp=true${defaultParams}&isLinkParametersEnabled=true${prefixParams}#/embed/viz/${liveboardId}`,
197
+ );
198
+ });
199
+ });
200
+
201
+ test('should set isLinkParametersEnabled to false in url', async () => {
202
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
203
+ isLinkParametersEnabled: false,
204
+ ...defaultViewConfig,
205
+ liveboardId,
206
+ } as LiveboardViewConfig);
207
+ liveboardEmbed.render();
208
+ await executeAfterWait(() => {
209
+ expectUrlMatchesWithParams(
210
+ getIFrameSrc(),
211
+ `http://${thoughtSpotHost}/?embedApp=true${defaultParams}&isLinkParametersEnabled=false${prefixParams}#/embed/viz/${liveboardId}`,
212
+ );
213
+ });
214
+ });
215
+
186
216
  test('should set visible actions as empty array', async () => {
187
217
  const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
188
218
  visibleActions: [],
@@ -477,6 +477,7 @@ export class LiveboardEmbed extends V1Embed {
477
477
  isLiveboardStylingAndGroupingEnabled,
478
478
  isPNGInScheduledEmailsEnabled = false,
479
479
  showSpotterLimitations,
480
+ isLinkParametersEnabled,
480
481
  } = this.viewConfig;
481
482
 
482
483
  const preventLiveboardFilterRemoval = this.viewConfig.preventLiveboardFilterRemoval
@@ -552,6 +553,10 @@ export class LiveboardEmbed extends V1Embed {
552
553
  params[Param.ShowSpotterLimitations] = showSpotterLimitations;
553
554
  }
554
555
 
556
+ if (isLinkParametersEnabled !== undefined) {
557
+ params[Param.isLinkParametersEnabled] = isLinkParametersEnabled;
558
+ }
559
+
555
560
  params[Param.LiveboardHeaderSticky] = isLiveboardHeaderSticky;
556
561
  params[Param.LiveboardHeaderV2] = isLiveboardCompactHeaderEnabled;
557
562
  params[Param.ShowLiveboardVerifiedBadge] = showLiveboardVerifiedBadge;
@@ -3345,4 +3345,115 @@ describe('Unit test case for ts embed', () => {
3345
3345
  expect(searchEmbed.isEmbedContainerLoaded).toBe(true);
3346
3346
  });
3347
3347
  });
3348
+
3349
+ describe('Online event listener registration after auth failure', () => {
3350
+ beforeAll(() => {
3351
+ init({
3352
+ thoughtSpotHost: 'tshost',
3353
+ authType: AuthType.None,
3354
+ loginFailedMessage: 'Not logged in',
3355
+ });
3356
+ });
3357
+
3358
+ test('should register online event listener when authentication fails', async () => {
3359
+ const addEventListenerSpy = jest.spyOn(window, 'addEventListener');
3360
+ jest.spyOn(baseInstance, 'getAuthPromise').mockRejectedValueOnce(
3361
+ new Error('Auth failed'),
3362
+ );
3363
+ const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
3364
+ addEventListenerSpy.mockClear();
3365
+ await searchEmbed.render();
3366
+ await executeAfterWait(() => {
3367
+ expect(getRootEl().innerHTML).toContain('Not logged in');
3368
+ const onlineListenerCalls = addEventListenerSpy.mock.calls.filter(
3369
+ (call) => call[0] === 'online',
3370
+ );
3371
+ expect(onlineListenerCalls).toHaveLength(1);
3372
+ const offlineListenerCalls = addEventListenerSpy.mock.calls.filter(
3373
+ (call) => call[0] === 'offline',
3374
+ );
3375
+ expect(offlineListenerCalls).toHaveLength(1);
3376
+ const messageListenerCalls = addEventListenerSpy.mock.calls.filter(
3377
+ (call) => call[0] === 'message',
3378
+ );
3379
+ expect(messageListenerCalls).toHaveLength(0);
3380
+ });
3381
+
3382
+ addEventListenerSpy.mockRestore();
3383
+ });
3384
+
3385
+ test('should attempt to trigger reload when online event occurs after auth failure', async () => {
3386
+ jest.spyOn(baseInstance, 'getAuthPromise').mockRejectedValueOnce(
3387
+ new Error('Auth failed'),
3388
+ );
3389
+ const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
3390
+ const triggerSpy = jest.spyOn(searchEmbed, 'trigger').mockResolvedValue(null);
3391
+ await searchEmbed.render();
3392
+
3393
+ await executeAfterWait(() => {
3394
+ expect(getRootEl().innerHTML).toContain('Not logged in');
3395
+ triggerSpy.mockClear();
3396
+ const onlineEvent = new Event('online');
3397
+ window.dispatchEvent(onlineEvent);
3398
+ expect(triggerSpy).toHaveBeenCalledWith(HostEvent.Reload);
3399
+ });
3400
+
3401
+ triggerSpy.mockReset();
3402
+ });
3403
+
3404
+ test('should handle online event gracefully when no iframe exists', async () => {
3405
+ jest.spyOn(baseInstance, 'getAuthPromise').mockRejectedValueOnce(
3406
+ new Error('Auth failed'),
3407
+ );
3408
+ const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
3409
+ const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
3410
+ await searchEmbed.render();
3411
+ await executeAfterWait(() => {
3412
+ expect(getRootEl().innerHTML).toContain('Not logged in');
3413
+ const onlineEvent = new Event('online');
3414
+ expect(() => {
3415
+ window.dispatchEvent(onlineEvent);
3416
+ }).not.toThrow();
3417
+ });
3418
+
3419
+ errorSpy.mockReset();
3420
+ });
3421
+
3422
+ test('should register all event listeners when authentication succeeds', async () => {
3423
+ const addEventListenerSpy = jest.spyOn(window, 'addEventListener');
3424
+ jest.spyOn(baseInstance, 'getAuthPromise').mockResolvedValueOnce(true);
3425
+ const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
3426
+ addEventListenerSpy.mockClear();
3427
+ await searchEmbed.render();
3428
+ await executeAfterWait(() => {
3429
+ const onlineListenerCalls = addEventListenerSpy.mock.calls.filter(
3430
+ (call) => call[0] === 'online',
3431
+ );
3432
+ expect(onlineListenerCalls).toHaveLength(1);
3433
+ const offlineListenerCalls = addEventListenerSpy.mock.calls.filter(
3434
+ (call) => call[0] === 'offline',
3435
+ );
3436
+ expect(offlineListenerCalls).toHaveLength(1);
3437
+ const messageListenerCalls = addEventListenerSpy.mock.calls.filter(
3438
+ (call) => call[0] === 'message',
3439
+ );
3440
+ expect(messageListenerCalls).toHaveLength(1);
3441
+ });
3442
+
3443
+ addEventListenerSpy.mockRestore();
3444
+ });
3445
+ test('should successfully trigger reload when online event occurs after auth success', async () => {
3446
+ jest.spyOn(baseInstance, 'getAuthPromise').mockResolvedValueOnce(true);
3447
+ const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
3448
+ const triggerSpy = jest.spyOn(searchEmbed, 'trigger').mockResolvedValue({} as any);
3449
+ await searchEmbed.render();
3450
+ await executeAfterWait(() => {
3451
+ triggerSpy.mockClear();
3452
+ const onlineEvent = new Event('online');
3453
+ window.dispatchEvent(onlineEvent);
3454
+ expect(triggerSpy).toHaveBeenCalledWith(HostEvent.Reload);
3455
+ });
3456
+ triggerSpy.mockReset();
3457
+ });
3458
+ });
3348
3459
  });
@@ -312,13 +312,36 @@ export class TsEmbed {
312
312
  private subscribedListeners: Record<string, any> = {};
313
313
 
314
314
  /**
315
- * Adds a global event listener to window for "message" events.
316
- * ThoughtSpot detects if a particular event is targeted to this
317
- * embed instance through an identifier contained in the payload,
318
- * and executes the registered callbacks accordingly.
315
+ * Subscribe to network events (online/offline) that should
316
+ * work regardless of auth status
319
317
  */
320
- private subscribeToEvents() {
321
- this.unsubscribeToEvents();
318
+ private subscribeToNetworkEvents() {
319
+ this.unsubscribeToNetworkEvents();
320
+
321
+ const onlineEventListener = (e: Event) => {
322
+ this.trigger(HostEvent.Reload);
323
+ };
324
+ window.addEventListener('online', onlineEventListener);
325
+
326
+ const offlineEventListener = (e: Event) => {
327
+ const offlineWarning = ERROR_MESSAGE.OFFLINE_WARNING;
328
+ this.executeCallbacks(EmbedEvent.Error, {
329
+ offlineWarning,
330
+ });
331
+ logger.warn(offlineWarning);
332
+ };
333
+ window.addEventListener('offline', offlineEventListener);
334
+
335
+ this.subscribedListeners.online = onlineEventListener;
336
+ this.subscribedListeners.offline = offlineEventListener;
337
+ }
338
+
339
+ /**
340
+ * Subscribe to message events that depend on successful iframe setup
341
+ */
342
+ private subscribeToMessageEvents() {
343
+ this.unsubscribeToMessageEvents();
344
+
322
345
  const messageEventListener = (event: MessageEvent<any>) => {
323
346
  const eventType = this.getEventType(event);
324
347
  const eventPort = this.getEventPort(event);
@@ -338,25 +361,37 @@ export class TsEmbed {
338
361
  };
339
362
  window.addEventListener('message', messageEventListener);
340
363
 
341
- const onlineEventListener = (e: Event) => {
342
- this.trigger(HostEvent.Reload);
343
- };
344
- window.addEventListener('online', onlineEventListener);
364
+ this.subscribedListeners.message = messageEventListener;
365
+ }
366
+ /**
367
+ * Adds event listeners for both network and message events.
368
+ * This maintains backward compatibility with the existing method.
369
+ * Adds a global event listener to window for "message" events.
370
+ * ThoughtSpot detects if a particular event is targeted to this
371
+ * embed instance through an identifier contained in the payload,
372
+ * and executes the registered callbacks accordingly.
373
+ */
374
+ private subscribeToEvents() {
375
+ this.subscribeToNetworkEvents();
376
+ this.subscribeToMessageEvents();
377
+ }
345
378
 
346
- const offlineEventListener = (e: Event) => {
347
- const offlineWarning = 'Network not Detected. Embed is offline. Please reconnect and refresh';
348
- this.executeCallbacks(EmbedEvent.Error, {
349
- offlineWarning,
350
- });
351
- logger.warn(offlineWarning);
352
- };
353
- window.addEventListener('offline', offlineEventListener);
379
+ private unsubscribeToNetworkEvents() {
380
+ if (this.subscribedListeners.online) {
381
+ window.removeEventListener('online', this.subscribedListeners.online);
382
+ delete this.subscribedListeners.online;
383
+ }
384
+ if (this.subscribedListeners.offline) {
385
+ window.removeEventListener('offline', this.subscribedListeners.offline);
386
+ delete this.subscribedListeners.offline;
387
+ }
388
+ }
354
389
 
355
- this.subscribedListeners = {
356
- message: messageEventListener,
357
- online: onlineEventListener,
358
- offline: offlineEventListener,
359
- };
390
+ private unsubscribeToMessageEvents() {
391
+ if (this.subscribedListeners.message) {
392
+ window.removeEventListener('message', this.subscribedListeners.message);
393
+ delete this.subscribedListeners.message;
394
+ }
360
395
  }
361
396
 
362
397
  private unsubscribeToEvents() {
@@ -798,6 +833,9 @@ export class TsEmbed {
798
833
 
799
834
  uploadMixpanelEvent(MIXPANEL_EVENT.VISUAL_SDK_RENDER_START);
800
835
 
836
+ // Always subscribe to network events, regardless of auth status
837
+ this.subscribeToNetworkEvents();
838
+
801
839
  return getAuthPromise()
802
840
  ?.then((isLoggedIn: boolean) => {
803
841
  if (!isLoggedIn) {
@@ -843,7 +881,9 @@ export class TsEmbed {
843
881
  el.remove();
844
882
  });
845
883
  }
846
- this.subscribeToEvents();
884
+ // Subscribe to message events only after successful
885
+ // auth and iframe setup
886
+ this.subscribeToMessageEvents();
847
887
  })
848
888
  .catch((error) => {
849
889
  nextInQueue();
@@ -1231,6 +1271,7 @@ export class TsEmbed {
1231
1271
  public async trigger<HostEventT extends HostEvent, PayloadT>(
1232
1272
  messageType: HostEventT,
1233
1273
  data: TriggerPayload<PayloadT, HostEventT> = {} as any,
1274
+ context: any = {},
1234
1275
  ): Promise<TriggerResponse<PayloadT, HostEventT>> {
1235
1276
  uploadMixpanelEvent(`${MIXPANEL_EVENT.VISUAL_SDK_TRIGGER}-${messageType}`);
1236
1277
 
@@ -1243,8 +1284,18 @@ export class TsEmbed {
1243
1284
  this.handleError('Host event type is undefined');
1244
1285
  return null;
1245
1286
  }
1287
+
1288
+ // Check if iframe exists before triggering -
1289
+ // this prevents the error when auth fails
1290
+ if (!this.iFrame) {
1291
+ logger.debug(
1292
+ `Cannot trigger ${messageType} - iframe not available (likely due to auth failure)`,
1293
+ );
1294
+ return null;
1295
+ }
1296
+
1246
1297
  // send an empty object, this is needed for liveboard default handlers
1247
- return this.hostEventClient.triggerHostEvent(messageType, data);
1298
+ return this.hostEventClient.triggerHostEvent(messageType, data, context);
1248
1299
  }
1249
1300
 
1250
1301
  /**
package/src/errors.ts CHANGED
@@ -19,6 +19,7 @@ export const ERROR_MESSAGE = {
19
19
  MISSING_REPORTING_OBSERVER: 'ReportingObserver not supported',
20
20
  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',
21
21
  SPOTTER_AGENT_NOT_INITIALIZED: 'SpotterAgent not initialized',
22
+ OFFLINE_WARNING : 'Network not Detected. Embed is offline. Please reconnect and refresh',
22
23
  };
23
24
 
24
25
  export const CUSTOM_ACTIONS_ERROR_MESSAGE = {