@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.
- package/cjs/package.json +2 -2
- package/cjs/src/embed/app.d.ts +20 -0
- package/cjs/src/embed/app.d.ts.map +1 -1
- package/cjs/src/embed/app.js +7 -1
- package/cjs/src/embed/app.js.map +1 -1
- package/cjs/src/embed/app.spec.js +52 -0
- package/cjs/src/embed/app.spec.js.map +1 -1
- package/cjs/src/embed/hostEventClient/contracts.d.ts +9 -2
- package/cjs/src/embed/hostEventClient/contracts.d.ts.map +1 -1
- package/cjs/src/embed/hostEventClient/contracts.js +9 -1
- package/cjs/src/embed/hostEventClient/contracts.js.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.d.ts +5 -5
- package/cjs/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.js +6 -6
- package/cjs/src/embed/hostEventClient/host-event-client.js.map +1 -1
- package/cjs/src/embed/liveboard.d.ts +2 -2
- package/cjs/src/embed/liveboard.d.ts.map +1 -1
- package/cjs/src/embed/liveboard.js +6 -3
- package/cjs/src/embed/liveboard.js.map +1 -1
- package/cjs/src/embed/liveboard.spec.js +22 -0
- package/cjs/src/embed/liveboard.spec.js.map +1 -1
- package/cjs/src/embed/ts-embed.d.ts +15 -2
- package/cjs/src/embed/ts-embed.d.ts.map +1 -1
- package/cjs/src/embed/ts-embed.js +63 -23
- package/cjs/src/embed/ts-embed.js.map +1 -1
- package/cjs/src/embed/ts-embed.spec.js +83 -0
- package/cjs/src/embed/ts-embed.spec.js.map +1 -1
- package/cjs/src/errors.d.ts +1 -0
- package/cjs/src/errors.d.ts.map +1 -1
- package/cjs/src/errors.js +1 -0
- package/cjs/src/errors.js.map +1 -1
- package/cjs/src/types.d.ts +119 -61
- package/cjs/src/types.d.ts.map +1 -1
- package/cjs/src/types.js +103 -60
- package/cjs/src/types.js.map +1 -1
- package/cjs/src/utils/processTrigger.d.ts +3 -1
- package/cjs/src/utils/processTrigger.d.ts.map +1 -1
- package/cjs/src/utils/processTrigger.js +4 -2
- package/cjs/src/utils/processTrigger.js.map +1 -1
- package/dist/{index-BpSohedu.js → index-CWQnMX2L.js} +1 -1
- package/dist/index-Djtv-y7A.js +7371 -0
- package/dist/index-UY-4yjBN.js +7371 -0
- package/dist/src/embed/app.d.ts +20 -0
- package/dist/src/embed/app.d.ts.map +1 -1
- package/dist/src/embed/hostEventClient/contracts.d.ts +9 -2
- package/dist/src/embed/hostEventClient/contracts.d.ts.map +1 -1
- package/dist/src/embed/hostEventClient/host-event-client.d.ts +5 -5
- package/dist/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/dist/src/embed/liveboard.d.ts +2 -2
- package/dist/src/embed/liveboard.d.ts.map +1 -1
- package/dist/src/embed/ts-embed.d.ts +15 -2
- package/dist/src/embed/ts-embed.d.ts.map +1 -1
- package/dist/src/errors.d.ts +1 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/types.d.ts +119 -61
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/processTrigger.d.ts +3 -1
- package/dist/src/utils/processTrigger.d.ts.map +1 -1
- package/dist/tsembed-react.es.js +202 -99
- package/dist/tsembed-react.js +201 -98
- package/dist/tsembed.es.js +202 -99
- package/dist/tsembed.js +201 -98
- package/dist/visual-embed-sdk-react-full.d.ts +164 -68
- package/dist/visual-embed-sdk-react.d.ts +164 -68
- package/dist/visual-embed-sdk.d.ts +164 -68
- package/lib/package.json +2 -2
- package/lib/src/embed/app.d.ts +20 -0
- package/lib/src/embed/app.d.ts.map +1 -1
- package/lib/src/embed/app.js +7 -1
- package/lib/src/embed/app.js.map +1 -1
- package/lib/src/embed/app.spec.js +52 -0
- package/lib/src/embed/app.spec.js.map +1 -1
- package/lib/src/embed/hostEventClient/contracts.d.ts +9 -2
- package/lib/src/embed/hostEventClient/contracts.d.ts.map +1 -1
- package/lib/src/embed/hostEventClient/contracts.js +8 -0
- package/lib/src/embed/hostEventClient/contracts.js.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.d.ts +5 -5
- package/lib/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.js +6 -6
- package/lib/src/embed/hostEventClient/host-event-client.js.map +1 -1
- package/lib/src/embed/liveboard.d.ts +2 -2
- package/lib/src/embed/liveboard.d.ts.map +1 -1
- package/lib/src/embed/liveboard.js +6 -3
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/embed/liveboard.spec.js +22 -0
- package/lib/src/embed/liveboard.spec.js.map +1 -1
- package/lib/src/embed/ts-embed.d.ts +15 -2
- package/lib/src/embed/ts-embed.d.ts.map +1 -1
- package/lib/src/embed/ts-embed.js +63 -23
- package/lib/src/embed/ts-embed.js.map +1 -1
- package/lib/src/embed/ts-embed.spec.js +83 -0
- package/lib/src/embed/ts-embed.spec.js.map +1 -1
- package/lib/src/errors.d.ts +1 -0
- package/lib/src/errors.d.ts.map +1 -1
- package/lib/src/errors.js +1 -0
- package/lib/src/errors.js.map +1 -1
- package/lib/src/types.d.ts +119 -61
- package/lib/src/types.d.ts.map +1 -1
- package/lib/src/types.js +103 -60
- package/lib/src/types.js.map +1 -1
- package/lib/src/utils/processTrigger.d.ts +3 -1
- package/lib/src/utils/processTrigger.d.ts.map +1 -1
- package/lib/src/utils/processTrigger.js +4 -2
- package/lib/src/utils/processTrigger.js.map +1 -1
- package/package.json +2 -2
- package/src/embed/app.spec.ts +73 -0
- package/src/embed/app.ts +30 -0
- package/src/embed/hostEventClient/contracts.ts +11 -3
- package/src/embed/hostEventClient/host-event-client.ts +10 -5
- package/src/embed/liveboard.spec.ts +30 -0
- package/src/embed/liveboard.ts +10 -4
- package/src/embed/ts-embed.spec.ts +111 -0
- package/src/embed/ts-embed.ts +79 -27
- package/src/errors.ts +1 -0
- package/src/types.ts +119 -61
- 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
|
-
|
|
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: [],
|
package/src/embed/liveboard.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
});
|
package/src/embed/ts-embed.ts
CHANGED
|
@@ -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
|
-
*
|
|
316
|
-
*
|
|
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
|
|
321
|
-
this.
|
|
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
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 = {
|