@thoughtspot/visual-embed-sdk 1.42.0 → 1.42.1-HE2.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 (116) 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/contracts.d.ts +9 -2
  9. package/cjs/src/embed/hostEventClient/contracts.d.ts.map +1 -1
  10. package/cjs/src/embed/hostEventClient/contracts.js +9 -1
  11. package/cjs/src/embed/hostEventClient/contracts.js.map +1 -1
  12. package/cjs/src/embed/hostEventClient/host-event-client.d.ts +5 -5
  13. package/cjs/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  14. package/cjs/src/embed/hostEventClient/host-event-client.js +6 -6
  15. package/cjs/src/embed/hostEventClient/host-event-client.js.map +1 -1
  16. package/cjs/src/embed/liveboard.d.ts +2 -2
  17. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  18. package/cjs/src/embed/liveboard.js +6 -3
  19. package/cjs/src/embed/liveboard.js.map +1 -1
  20. package/cjs/src/embed/liveboard.spec.js +22 -0
  21. package/cjs/src/embed/liveboard.spec.js.map +1 -1
  22. package/cjs/src/embed/ts-embed.d.ts +15 -2
  23. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  24. package/cjs/src/embed/ts-embed.js +63 -23
  25. package/cjs/src/embed/ts-embed.js.map +1 -1
  26. package/cjs/src/embed/ts-embed.spec.js +83 -0
  27. package/cjs/src/embed/ts-embed.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/types.d.ts +119 -61
  33. package/cjs/src/types.d.ts.map +1 -1
  34. package/cjs/src/types.js +103 -60
  35. package/cjs/src/types.js.map +1 -1
  36. package/cjs/src/utils/processTrigger.d.ts +3 -1
  37. package/cjs/src/utils/processTrigger.d.ts.map +1 -1
  38. package/cjs/src/utils/processTrigger.js +4 -2
  39. package/cjs/src/utils/processTrigger.js.map +1 -1
  40. package/dist/{index-BpSohedu.js → index-CWQnMX2L.js} +1 -1
  41. package/dist/index-Djtv-y7A.js +7371 -0
  42. package/dist/index-UY-4yjBN.js +7371 -0
  43. package/dist/src/embed/app.d.ts +20 -0
  44. package/dist/src/embed/app.d.ts.map +1 -1
  45. package/dist/src/embed/hostEventClient/contracts.d.ts +9 -2
  46. package/dist/src/embed/hostEventClient/contracts.d.ts.map +1 -1
  47. package/dist/src/embed/hostEventClient/host-event-client.d.ts +5 -5
  48. package/dist/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  49. package/dist/src/embed/liveboard.d.ts +2 -2
  50. package/dist/src/embed/liveboard.d.ts.map +1 -1
  51. package/dist/src/embed/ts-embed.d.ts +15 -2
  52. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  53. package/dist/src/errors.d.ts +1 -0
  54. package/dist/src/errors.d.ts.map +1 -1
  55. package/dist/src/types.d.ts +119 -61
  56. package/dist/src/types.d.ts.map +1 -1
  57. package/dist/src/utils/processTrigger.d.ts +3 -1
  58. package/dist/src/utils/processTrigger.d.ts.map +1 -1
  59. package/dist/tsembed-react.es.js +202 -99
  60. package/dist/tsembed-react.js +201 -98
  61. package/dist/tsembed.es.js +202 -99
  62. package/dist/tsembed.js +201 -98
  63. package/dist/visual-embed-sdk-react-full.d.ts +164 -68
  64. package/dist/visual-embed-sdk-react.d.ts +164 -68
  65. package/dist/visual-embed-sdk.d.ts +164 -68
  66. package/lib/package.json +2 -2
  67. package/lib/src/embed/app.d.ts +20 -0
  68. package/lib/src/embed/app.d.ts.map +1 -1
  69. package/lib/src/embed/app.js +7 -1
  70. package/lib/src/embed/app.js.map +1 -1
  71. package/lib/src/embed/app.spec.js +52 -0
  72. package/lib/src/embed/app.spec.js.map +1 -1
  73. package/lib/src/embed/hostEventClient/contracts.d.ts +9 -2
  74. package/lib/src/embed/hostEventClient/contracts.d.ts.map +1 -1
  75. package/lib/src/embed/hostEventClient/contracts.js +8 -0
  76. package/lib/src/embed/hostEventClient/contracts.js.map +1 -1
  77. package/lib/src/embed/hostEventClient/host-event-client.d.ts +5 -5
  78. package/lib/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  79. package/lib/src/embed/hostEventClient/host-event-client.js +6 -6
  80. package/lib/src/embed/hostEventClient/host-event-client.js.map +1 -1
  81. package/lib/src/embed/liveboard.d.ts +2 -2
  82. package/lib/src/embed/liveboard.d.ts.map +1 -1
  83. package/lib/src/embed/liveboard.js +6 -3
  84. package/lib/src/embed/liveboard.js.map +1 -1
  85. package/lib/src/embed/liveboard.spec.js +22 -0
  86. package/lib/src/embed/liveboard.spec.js.map +1 -1
  87. package/lib/src/embed/ts-embed.d.ts +15 -2
  88. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  89. package/lib/src/embed/ts-embed.js +63 -23
  90. package/lib/src/embed/ts-embed.js.map +1 -1
  91. package/lib/src/embed/ts-embed.spec.js +83 -0
  92. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  93. package/lib/src/errors.d.ts +1 -0
  94. package/lib/src/errors.d.ts.map +1 -1
  95. package/lib/src/errors.js +1 -0
  96. package/lib/src/errors.js.map +1 -1
  97. package/lib/src/types.d.ts +119 -61
  98. package/lib/src/types.d.ts.map +1 -1
  99. package/lib/src/types.js +103 -60
  100. package/lib/src/types.js.map +1 -1
  101. package/lib/src/utils/processTrigger.d.ts +3 -1
  102. package/lib/src/utils/processTrigger.d.ts.map +1 -1
  103. package/lib/src/utils/processTrigger.js +4 -2
  104. package/lib/src/utils/processTrigger.js.map +1 -1
  105. package/package.json +2 -2
  106. package/src/embed/app.spec.ts +73 -0
  107. package/src/embed/app.ts +30 -0
  108. package/src/embed/hostEventClient/contracts.ts +11 -3
  109. package/src/embed/hostEventClient/host-event-client.ts +10 -5
  110. package/src/embed/liveboard.spec.ts +30 -0
  111. package/src/embed/liveboard.ts +10 -4
  112. package/src/embed/ts-embed.spec.ts +111 -0
  113. package/src/embed/ts-embed.ts +79 -27
  114. package/src/errors.ts +1 -0
  115. package/src/types.ts +119 -61
  116. package/src/utils/processTrigger.ts +6 -2
@@ -8,6 +8,7 @@ import {
8
8
  UIPassthroughResponse,
9
9
  TriggerPayload,
10
10
  TriggerResponse,
11
+ ContextType,
11
12
  } from './contracts';
12
13
 
13
14
  export class HostEventClient {
@@ -23,7 +24,7 @@ export class HostEventClient {
23
24
  * @param {any} data Data to send with the host event
24
25
  * @returns {Promise<any>} - the response from the process trigger
25
26
  */
26
- protected async processTrigger(message: HostEvent, data: any): Promise<any> {
27
+ protected async processTrigger(message: HostEvent, data: any, context?: ContextType): Promise<any> {
27
28
  if (!this.iFrame) {
28
29
  throw new Error('Iframe element is not set');
29
30
  }
@@ -34,6 +35,7 @@ export class HostEventClient {
34
35
  message,
35
36
  thoughtspotHost,
36
37
  data,
38
+ context,
37
39
  );
38
40
  }
39
41
 
@@ -65,8 +67,9 @@ export class HostEventClient {
65
67
  public async hostEventFallback(
66
68
  hostEvent: HostEvent,
67
69
  data: any,
70
+ context?: ContextType,
68
71
  ): Promise<any> {
69
- return this.processTrigger(hostEvent, data);
72
+ return this.processTrigger(hostEvent, data, context);
70
73
  }
71
74
 
72
75
  /**
@@ -91,7 +94,7 @@ export class HostEventClient {
91
94
 
92
95
  protected async handlePinEvent(
93
96
  payload: HostEventRequest<HostEvent.Pin>,
94
- ): Promise<HostEventResponse<HostEvent.Pin>> {
97
+ ): Promise<HostEventResponse<HostEvent.Pin, ContextType>> {
95
98
  if (!payload || !('newVizName' in payload)) {
96
99
  return this.hostEventFallback(HostEvent.Pin, payload);
97
100
  }
@@ -132,10 +135,12 @@ export class HostEventClient {
132
135
  public async triggerHostEvent<
133
136
  HostEventT extends HostEvent,
134
137
  PayloadT,
138
+ ContextT extends ContextType,
135
139
  >(
136
140
  hostEvent: HostEventT,
137
141
  payload?: TriggerPayload<PayloadT, HostEventT>,
138
- ): Promise<TriggerResponse<PayloadT, HostEventT>> {
142
+ context?: ContextT,
143
+ ): Promise<TriggerResponse<PayloadT, HostEventT, ContextType>> {
139
144
  switch (hostEvent) {
140
145
  case HostEvent.Pin:
141
146
  return this.handlePinEvent(payload as HostEventRequest<HostEvent.Pin>) as any;
@@ -144,7 +149,7 @@ export class HostEventClient {
144
149
  payload as HostEventRequest<HostEvent.SaveAnswer>,
145
150
  ) as any;
146
151
  default:
147
- return this.hostEventFallback(hostEvent, payload);
152
+ return this.hostEventFallback(hostEvent, payload, context);
148
153
  }
149
154
  }
150
155
  }
@@ -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: [],
@@ -25,7 +25,7 @@ import { calculateVisibleElementData, getQueryParamString, isUndefined } from '.
25
25
  import { getAuthPromise } from './base';
26
26
  import { TsEmbed, V1Embed } from './ts-embed';
27
27
  import { addPreviewStylesIfNotPresent } from '../utils/global-styles';
28
- import { TriggerPayload, TriggerResponse } from './hostEventClient/contracts';
28
+ import { ContextType, TriggerPayload, TriggerResponse } from './hostEventClient/contracts';
29
29
  import { logger } from '../utils/logger';
30
30
 
31
31
 
@@ -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;
@@ -733,10 +738,11 @@ export class LiveboardEmbed extends V1Embed {
733
738
  * @param {any} data The payload to send with the message
734
739
  * @returns A promise that resolves with the response from the embedded app
735
740
  */
736
- public trigger<HostEventT extends HostEvent, PayloadT>(
741
+ public trigger<HostEventT extends HostEvent, PayloadT, ContextT extends ContextType>(
737
742
  messageType: HostEventT,
738
743
  data: TriggerPayload<PayloadT, HostEventT> = ({} as any),
739
- ): Promise<TriggerResponse<PayloadT, HostEventT>> {
744
+ context?: ContextT,
745
+ ): Promise<TriggerResponse<PayloadT, HostEventT, ContextT>> {
740
746
  const dataWithVizId: any = data;
741
747
  if (messageType === HostEvent.SetActiveTab) {
742
748
  this.setActiveTab(data as { tabId: string });
@@ -745,7 +751,7 @@ export class LiveboardEmbed extends V1Embed {
745
751
  if (typeof dataWithVizId === 'object' && this.viewConfig.vizId) {
746
752
  dataWithVizId.vizId = this.viewConfig.vizId;
747
753
  }
748
- return super.trigger(messageType, dataWithVizId);
754
+ return super.trigger(messageType, dataWithVizId, context);
749
755
  }
750
756
  /**
751
757
  * Destroys the ThoughtSpot embed, and remove any nodes from the DOM.
@@ -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
  });
@@ -10,6 +10,7 @@ import isEqual from 'lodash/isEqual';
10
10
  import isEmpty from 'lodash/isEmpty';
11
11
  import isObject from 'lodash/isObject';
12
12
  import {
13
+ ContextType,
13
14
  TriggerPayload,
14
15
  TriggerResponse,
15
16
  UIPassthroughArrayResponse,
@@ -312,13 +313,36 @@ export class TsEmbed {
312
313
  private subscribedListeners: Record<string, any> = {};
313
314
 
314
315
  /**
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.
316
+ * Subscribe to network events (online/offline) that should
317
+ * work regardless of auth status
319
318
  */
320
- private subscribeToEvents() {
321
- this.unsubscribeToEvents();
319
+ private subscribeToNetworkEvents() {
320
+ this.unsubscribeToNetworkEvents();
321
+
322
+ const onlineEventListener = (e: Event) => {
323
+ this.trigger(HostEvent.Reload);
324
+ };
325
+ window.addEventListener('online', onlineEventListener);
326
+
327
+ const offlineEventListener = (e: Event) => {
328
+ const offlineWarning = ERROR_MESSAGE.OFFLINE_WARNING;
329
+ this.executeCallbacks(EmbedEvent.Error, {
330
+ offlineWarning,
331
+ });
332
+ logger.warn(offlineWarning);
333
+ };
334
+ window.addEventListener('offline', offlineEventListener);
335
+
336
+ this.subscribedListeners.online = onlineEventListener;
337
+ this.subscribedListeners.offline = offlineEventListener;
338
+ }
339
+
340
+ /**
341
+ * Subscribe to message events that depend on successful iframe setup
342
+ */
343
+ private subscribeToMessageEvents() {
344
+ this.unsubscribeToMessageEvents();
345
+
322
346
  const messageEventListener = (event: MessageEvent<any>) => {
323
347
  const eventType = this.getEventType(event);
324
348
  const eventPort = this.getEventPort(event);
@@ -338,25 +362,37 @@ export class TsEmbed {
338
362
  };
339
363
  window.addEventListener('message', messageEventListener);
340
364
 
341
- const onlineEventListener = (e: Event) => {
342
- this.trigger(HostEvent.Reload);
343
- };
344
- window.addEventListener('online', onlineEventListener);
365
+ this.subscribedListeners.message = messageEventListener;
366
+ }
367
+ /**
368
+ * Adds event listeners for both network and message events.
369
+ * This maintains backward compatibility with the existing method.
370
+ * Adds a global event listener to window for "message" events.
371
+ * ThoughtSpot detects if a particular event is targeted to this
372
+ * embed instance through an identifier contained in the payload,
373
+ * and executes the registered callbacks accordingly.
374
+ */
375
+ private subscribeToEvents() {
376
+ this.subscribeToNetworkEvents();
377
+ this.subscribeToMessageEvents();
378
+ }
345
379
 
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);
380
+ private unsubscribeToNetworkEvents() {
381
+ if (this.subscribedListeners.online) {
382
+ window.removeEventListener('online', this.subscribedListeners.online);
383
+ delete this.subscribedListeners.online;
384
+ }
385
+ if (this.subscribedListeners.offline) {
386
+ window.removeEventListener('offline', this.subscribedListeners.offline);
387
+ delete this.subscribedListeners.offline;
388
+ }
389
+ }
354
390
 
355
- this.subscribedListeners = {
356
- message: messageEventListener,
357
- online: onlineEventListener,
358
- offline: offlineEventListener,
359
- };
391
+ private unsubscribeToMessageEvents() {
392
+ if (this.subscribedListeners.message) {
393
+ window.removeEventListener('message', this.subscribedListeners.message);
394
+ delete this.subscribedListeners.message;
395
+ }
360
396
  }
361
397
 
362
398
  private unsubscribeToEvents() {
@@ -798,6 +834,9 @@ export class TsEmbed {
798
834
 
799
835
  uploadMixpanelEvent(MIXPANEL_EVENT.VISUAL_SDK_RENDER_START);
800
836
 
837
+ // Always subscribe to network events, regardless of auth status
838
+ this.subscribeToNetworkEvents();
839
+
801
840
  return getAuthPromise()
802
841
  ?.then((isLoggedIn: boolean) => {
803
842
  if (!isLoggedIn) {
@@ -843,7 +882,9 @@ export class TsEmbed {
843
882
  el.remove();
844
883
  });
845
884
  }
846
- this.subscribeToEvents();
885
+ // Subscribe to message events only after successful
886
+ // auth and iframe setup
887
+ this.subscribeToMessageEvents();
847
888
  })
848
889
  .catch((error) => {
849
890
  nextInQueue();
@@ -1228,10 +1269,11 @@ export class TsEmbed {
1228
1269
  * @param {any} data The payload to send with the message
1229
1270
  * @returns A promise that resolves with the response from the embedded app
1230
1271
  */
1231
- public async trigger<HostEventT extends HostEvent, PayloadT>(
1272
+ public async trigger<HostEventT extends HostEvent, PayloadT, ContextT extends ContextType>(
1232
1273
  messageType: HostEventT,
1233
1274
  data: TriggerPayload<PayloadT, HostEventT> = {} as any,
1234
- ): Promise<TriggerResponse<PayloadT, HostEventT>> {
1275
+ context?: ContextT,
1276
+ ): Promise<TriggerResponse<PayloadT, HostEventT, ContextT>> {
1235
1277
  uploadMixpanelEvent(`${MIXPANEL_EVENT.VISUAL_SDK_TRIGGER}-${messageType}`);
1236
1278
 
1237
1279
  if (!this.isRendered) {
@@ -1243,8 +1285,18 @@ export class TsEmbed {
1243
1285
  this.handleError('Host event type is undefined');
1244
1286
  return null;
1245
1287
  }
1288
+
1289
+ // Check if iframe exists before triggering -
1290
+ // this prevents the error when auth fails
1291
+ if (!this.iFrame) {
1292
+ logger.debug(
1293
+ `Cannot trigger ${messageType} - iframe not available (likely due to auth failure)`,
1294
+ );
1295
+ return null;
1296
+ }
1297
+
1246
1298
  // send an empty object, this is needed for liveboard default handlers
1247
- return this.hostEventClient.triggerHostEvent(messageType, data);
1299
+ return this.hostEventClient.triggerHostEvent(messageType, data, context);
1248
1300
  }
1249
1301
 
1250
1302
  /**
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 = {