@thoughtspot/visual-embed-sdk 1.41.1 → 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.
- package/cjs/package.json +9 -9
- package/cjs/src/css-variables.d.ts +52 -14
- package/cjs/src/css-variables.d.ts.map +1 -1
- 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/bodyless-conversation.d.ts +1 -0
- package/cjs/src/embed/bodyless-conversation.d.ts.map +1 -1
- package/cjs/src/embed/bodyless-conversation.js +7 -3
- package/cjs/src/embed/bodyless-conversation.js.map +1 -1
- package/cjs/src/embed/conversation.d.ts +1 -0
- package/cjs/src/embed/conversation.d.ts.map +1 -1
- package/cjs/src/embed/conversation.js +7 -2
- package/cjs/src/embed/conversation.js.map +1 -1
- package/cjs/src/embed/hostEventClient/host-event-client.d.ts +3 -3
- 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 +1 -0
- package/cjs/src/embed/liveboard.d.ts.map +1 -1
- package/cjs/src/embed/liveboard.js +10 -2
- package/cjs/src/embed/liveboard.js.map +1 -1
- package/cjs/src/embed/liveboard.spec.js +35 -0
- package/cjs/src/embed/liveboard.spec.js.map +1 -1
- package/cjs/src/embed/sage.d.ts +1 -0
- package/cjs/src/embed/sage.d.ts.map +1 -1
- package/cjs/src/embed/sage.js +10 -6
- package/cjs/src/embed/sage.js.map +1 -1
- package/cjs/src/embed/search-bar.d.ts +1 -0
- package/cjs/src/embed/search-bar.d.ts.map +1 -1
- package/cjs/src/embed/search-bar.js +11 -7
- package/cjs/src/embed/search-bar.js.map +1 -1
- package/cjs/src/embed/search.d.ts +1 -0
- package/cjs/src/embed/search.d.ts.map +1 -1
- package/cjs/src/embed/search.js +7 -8
- package/cjs/src/embed/search.js.map +1 -1
- package/cjs/src/embed/ts-embed.d.ts +20 -5
- package/cjs/src/embed/ts-embed.d.ts.map +1 -1
- package/cjs/src/embed/ts-embed.js +84 -31
- 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/react/all-types-export.spec.js +1 -1
- package/cjs/src/react/all-types-export.spec.js.map +1 -1
- package/cjs/src/react/util.js.map +1 -1
- package/cjs/src/react/util.spec.d.ts +2 -0
- package/cjs/src/react/util.spec.d.ts.map +1 -0
- package/cjs/src/react/util.spec.js +78 -0
- package/cjs/src/react/util.spec.js.map +1 -0
- package/cjs/src/types.d.ts +153 -64
- package/cjs/src/types.d.ts.map +1 -1
- package/cjs/src/types.js +136 -62
- package/cjs/src/types.js.map +1 -1
- package/cjs/src/utils/processTrigger.d.ts +2 -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-DQueHwfQ.js → index-CWQnMX2L.js} +1 -1
- package/dist/index-Djtv-y7A.js +7371 -0
- package/dist/src/css-variables.d.ts +52 -14
- package/dist/src/css-variables.d.ts.map +1 -1
- package/dist/src/embed/app.d.ts +20 -0
- package/dist/src/embed/app.d.ts.map +1 -1
- package/dist/src/embed/bodyless-conversation.d.ts +1 -0
- package/dist/src/embed/bodyless-conversation.d.ts.map +1 -1
- package/dist/src/embed/conversation.d.ts +1 -0
- package/dist/src/embed/conversation.d.ts.map +1 -1
- package/dist/src/embed/hostEventClient/host-event-client.d.ts +3 -3
- package/dist/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
- package/dist/src/embed/liveboard.d.ts +1 -0
- package/dist/src/embed/liveboard.d.ts.map +1 -1
- package/dist/src/embed/sage.d.ts +1 -0
- package/dist/src/embed/sage.d.ts.map +1 -1
- package/dist/src/embed/search-bar.d.ts +1 -0
- package/dist/src/embed/search-bar.d.ts.map +1 -1
- package/dist/src/embed/search.d.ts +1 -0
- package/dist/src/embed/search.d.ts.map +1 -1
- package/dist/src/embed/ts-embed.d.ts +20 -5
- 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/react/util.spec.d.ts +2 -0
- package/dist/src/react/util.spec.d.ts.map +1 -0
- package/dist/src/types.d.ts +153 -64
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/processTrigger.d.ts +2 -1
- package/dist/src/utils/processTrigger.d.ts.map +1 -1
- package/dist/tsembed-react.es.js +294 -134
- package/dist/tsembed-react.js +293 -133
- package/dist/tsembed.es.js +294 -134
- package/dist/tsembed.js +293 -133
- package/dist/visual-embed-sdk-react-full.d.ts +9401 -9916
- package/dist/visual-embed-sdk-react.d.ts +9273 -9922
- package/dist/visual-embed-sdk.d.ts +9440 -9533
- package/lib/package.json +9 -9
- package/lib/src/css-variables.d.ts +52 -14
- package/lib/src/css-variables.d.ts.map +1 -1
- 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/bodyless-conversation.d.ts +1 -0
- package/lib/src/embed/bodyless-conversation.d.ts.map +1 -1
- package/lib/src/embed/bodyless-conversation.js +7 -3
- package/lib/src/embed/bodyless-conversation.js.map +1 -1
- package/lib/src/embed/conversation.d.ts +1 -0
- package/lib/src/embed/conversation.d.ts.map +1 -1
- package/lib/src/embed/conversation.js +7 -2
- package/lib/src/embed/conversation.js.map +1 -1
- package/lib/src/embed/hostEventClient/host-event-client.d.ts +3 -3
- 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 +1 -0
- package/lib/src/embed/liveboard.d.ts.map +1 -1
- package/lib/src/embed/liveboard.js +10 -2
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/embed/liveboard.spec.js +35 -0
- package/lib/src/embed/liveboard.spec.js.map +1 -1
- package/lib/src/embed/sage.d.ts +1 -0
- package/lib/src/embed/sage.d.ts.map +1 -1
- package/lib/src/embed/sage.js +10 -6
- package/lib/src/embed/sage.js.map +1 -1
- package/lib/src/embed/search-bar.d.ts +1 -0
- package/lib/src/embed/search-bar.d.ts.map +1 -1
- package/lib/src/embed/search-bar.js +11 -7
- package/lib/src/embed/search-bar.js.map +1 -1
- package/lib/src/embed/search.d.ts +1 -0
- package/lib/src/embed/search.d.ts.map +1 -1
- package/lib/src/embed/search.js +7 -8
- package/lib/src/embed/search.js.map +1 -1
- package/lib/src/embed/ts-embed.d.ts +20 -5
- package/lib/src/embed/ts-embed.d.ts.map +1 -1
- package/lib/src/embed/ts-embed.js +84 -31
- 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/react/all-types-export.spec.js +1 -1
- package/lib/src/react/all-types-export.spec.js.map +1 -1
- package/lib/src/react/util.js.map +1 -1
- package/lib/src/react/util.spec.d.ts +2 -0
- package/lib/src/react/util.spec.d.ts.map +1 -0
- package/lib/src/react/util.spec.js +76 -0
- package/lib/src/react/util.spec.js.map +1 -0
- package/lib/src/types.d.ts +153 -64
- package/lib/src/types.d.ts.map +1 -1
- package/lib/src/types.js +136 -62
- package/lib/src/types.js.map +1 -1
- package/lib/src/utils/processTrigger.d.ts +2 -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 +9 -9
- package/src/css-variables.ts +53 -16
- package/src/embed/app.spec.ts +73 -0
- package/src/embed/app.ts +30 -0
- package/src/embed/bodyless-conversation.ts +8 -3
- package/src/embed/conversation.ts +17 -2
- package/src/embed/hostEventClient/host-event-client.ts +7 -3
- package/src/embed/liveboard.spec.ts +44 -0
- package/src/embed/liveboard.ts +12 -1
- package/src/embed/sage.ts +14 -9
- package/src/embed/search-bar.tsx +14 -7
- package/src/embed/search.ts +18 -7
- package/src/embed/ts-embed.spec.ts +112 -1
- package/src/embed/ts-embed.ts +104 -37
- package/src/errors.ts +1 -0
- package/src/react/all-types-export.spec.ts +1 -1
- package/src/react/util.spec.tsx +88 -0
- package/src/react/util.ts +3 -3
- package/src/types.ts +211 -121
- package/src/utils/processTrigger.ts +5 -2
- package/lib/src/visual-embed-sdk.d.ts +0 -9779
package/src/embed/search.ts
CHANGED
|
@@ -331,7 +331,7 @@ export const HiddenActionItemByDefaultForSearchEmbed = [
|
|
|
331
331
|
];
|
|
332
332
|
|
|
333
333
|
export interface SearchAppInitData extends DefaultAppInitData {
|
|
334
|
-
|
|
334
|
+
searchOptions?: SearchOptions;
|
|
335
335
|
}
|
|
336
336
|
|
|
337
337
|
/**
|
|
@@ -381,7 +381,7 @@ export class SearchEmbed extends TsEmbed {
|
|
|
381
381
|
return { ...defaultAppInitData, ...this.getSearchInitData() };
|
|
382
382
|
}
|
|
383
383
|
|
|
384
|
-
protected
|
|
384
|
+
protected getEmbedParamsObject() {
|
|
385
385
|
const {
|
|
386
386
|
hideResults,
|
|
387
387
|
enableSearchAssist,
|
|
@@ -398,7 +398,7 @@ export class SearchEmbed extends TsEmbed {
|
|
|
398
398
|
collapseSearchBarInitially = false,
|
|
399
399
|
enableCustomColumnGroups = false,
|
|
400
400
|
isOnBeforeGetVizDataInterceptEnabled = false,
|
|
401
|
-
|
|
401
|
+
|
|
402
402
|
dataPanelCustomGroupsAccordionInitialState = DataPanelCustomColumnGroupsAccordionState.EXPAND_ALL,
|
|
403
403
|
focusSearchBarOnRender = true,
|
|
404
404
|
excludeRuntimeParametersfromURL,
|
|
@@ -443,7 +443,7 @@ export class SearchEmbed extends TsEmbed {
|
|
|
443
443
|
}
|
|
444
444
|
|
|
445
445
|
if (isOnBeforeGetVizDataInterceptEnabled) {
|
|
446
|
-
|
|
446
|
+
|
|
447
447
|
queryParams[Param.IsOnBeforeGetVizDataInterceptEnabled] = isOnBeforeGetVizDataInterceptEnabled;
|
|
448
448
|
}
|
|
449
449
|
|
|
@@ -460,7 +460,7 @@ export class SearchEmbed extends TsEmbed {
|
|
|
460
460
|
}
|
|
461
461
|
|
|
462
462
|
queryParams[Param.searchEmbed] = true;
|
|
463
|
-
|
|
463
|
+
|
|
464
464
|
queryParams[Param.CollapseSearchBarInitially] = collapseSearchBarInitially || collapseSearchBar;
|
|
465
465
|
queryParams[Param.EnableCustomColumnGroups] = enableCustomColumnGroups;
|
|
466
466
|
if (dataPanelCustomGroupsAccordionInitialState
|
|
@@ -468,12 +468,23 @@ export class SearchEmbed extends TsEmbed {
|
|
|
468
468
|
|| dataPanelCustomGroupsAccordionInitialState
|
|
469
469
|
=== DataPanelCustomColumnGroupsAccordionState.EXPAND_FIRST
|
|
470
470
|
) {
|
|
471
|
-
|
|
471
|
+
|
|
472
472
|
queryParams[Param.DataPanelCustomGroupsAccordionInitialState] = dataPanelCustomGroupsAccordionInitialState;
|
|
473
473
|
} else {
|
|
474
|
-
|
|
474
|
+
|
|
475
475
|
queryParams[Param.DataPanelCustomGroupsAccordionInitialState] = DataPanelCustomColumnGroupsAccordionState.EXPAND_ALL;
|
|
476
476
|
}
|
|
477
|
+
return queryParams;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
protected getEmbedParams() {
|
|
481
|
+
const {
|
|
482
|
+
runtimeParameters,
|
|
483
|
+
runtimeFilters,
|
|
484
|
+
excludeRuntimeParametersfromURL,
|
|
485
|
+
excludeRuntimeFiltersfromURL,
|
|
486
|
+
} = this.viewConfig;
|
|
487
|
+
const queryParams = this.getEmbedParamsObject();
|
|
477
488
|
let query = '';
|
|
478
489
|
const queryParamsString = getQueryParamString(queryParams, true);
|
|
479
490
|
if (queryParamsString) {
|
|
@@ -2488,7 +2488,7 @@ describe('Unit test case for ts embed', () => {
|
|
|
2488
2488
|
});
|
|
2489
2489
|
|
|
2490
2490
|
afterAll((): void => {
|
|
2491
|
-
window.location = location;
|
|
2491
|
+
window.location = location as any;
|
|
2492
2492
|
});
|
|
2493
2493
|
|
|
2494
2494
|
it('get url params for TS', () => {
|
|
@@ -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
|
@@ -199,11 +199,11 @@ export class TsEmbed {
|
|
|
199
199
|
uploadMixpanelEvent(MIXPANEL_EVENT.VISUAL_SDK_EMBED_CREATE, {
|
|
200
200
|
...viewConfig,
|
|
201
201
|
});
|
|
202
|
+
const embedConfig = getEmbedConfig();
|
|
203
|
+
this.embedConfig = embedConfig;
|
|
204
|
+
|
|
202
205
|
this.hostEventClient = new HostEventClient(this.iFrame);
|
|
203
|
-
|
|
204
206
|
this.isReadyForRenderPromise = getInitPromise().then(async () => {
|
|
205
|
-
const embedConfig = getEmbedConfig();
|
|
206
|
-
this.embedConfig = embedConfig;
|
|
207
207
|
if (!embedConfig.authTriggerContainer && !embedConfig.useEventForSAMLPopup) {
|
|
208
208
|
this.embedConfig.authTriggerContainer = domSelector;
|
|
209
209
|
}
|
|
@@ -312,13 +312,36 @@ export class TsEmbed {
|
|
|
312
312
|
private subscribedListeners: Record<string, any> = {};
|
|
313
313
|
|
|
314
314
|
/**
|
|
315
|
-
*
|
|
316
|
-
*
|
|
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
|
|
321
|
-
this.
|
|
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
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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() {
|
|
@@ -494,10 +529,10 @@ export class TsEmbed {
|
|
|
494
529
|
this.on(EmbedEvent.APP_INIT, this.appInitCb, { start: false }, true);
|
|
495
530
|
this.on(EmbedEvent.AuthExpire, this.updateAuthToken, { start: false }, true);
|
|
496
531
|
this.on(EmbedEvent.IdleSessionTimeout, this.idleSessionTimeout, { start: false }, true);
|
|
497
|
-
|
|
498
|
-
const embedListenerReadyHandler = this.createEmbedContainerHandler(EmbedEvent.EmbedListenerReady);
|
|
532
|
+
|
|
533
|
+
const embedListenerReadyHandler = this.createEmbedContainerHandler(EmbedEvent.EmbedListenerReady);
|
|
499
534
|
this.on(EmbedEvent.EmbedListenerReady, embedListenerReadyHandler, { start: false }, true);
|
|
500
|
-
|
|
535
|
+
|
|
501
536
|
const authInitHandler = this.createEmbedContainerHandler(EmbedEvent.AuthInit);
|
|
502
537
|
this.on(EmbedEvent.AuthInit, authInitHandler, { start: false }, true);
|
|
503
538
|
};
|
|
@@ -520,6 +555,12 @@ export class TsEmbed {
|
|
|
520
555
|
return `${basePath}#`;
|
|
521
556
|
}
|
|
522
557
|
|
|
558
|
+
protected getUpdateEmbedParamsObject() {
|
|
559
|
+
let queryParams = this.getEmbedParamsObject();
|
|
560
|
+
queryParams = { ...this.viewConfig, ...queryParams };
|
|
561
|
+
return queryParams;
|
|
562
|
+
}
|
|
563
|
+
|
|
523
564
|
/**
|
|
524
565
|
* Common query params set for all the embed modes.
|
|
525
566
|
* @param queryParams
|
|
@@ -702,10 +743,15 @@ export class TsEmbed {
|
|
|
702
743
|
}
|
|
703
744
|
|
|
704
745
|
protected getEmbedParams() {
|
|
705
|
-
const queryParams = this.
|
|
746
|
+
const queryParams = this.getEmbedParamsObject();
|
|
706
747
|
return getQueryParamString(queryParams);
|
|
707
748
|
}
|
|
708
749
|
|
|
750
|
+
protected getEmbedParamsObject() {
|
|
751
|
+
const params = this.getBaseQueryParams();
|
|
752
|
+
return params;
|
|
753
|
+
}
|
|
754
|
+
|
|
709
755
|
protected getRootIframeSrc() {
|
|
710
756
|
const query = this.getEmbedParams();
|
|
711
757
|
return this.getEmbedBasePath(query);
|
|
@@ -787,6 +833,9 @@ export class TsEmbed {
|
|
|
787
833
|
|
|
788
834
|
uploadMixpanelEvent(MIXPANEL_EVENT.VISUAL_SDK_RENDER_START);
|
|
789
835
|
|
|
836
|
+
// Always subscribe to network events, regardless of auth status
|
|
837
|
+
this.subscribeToNetworkEvents();
|
|
838
|
+
|
|
790
839
|
return getAuthPromise()
|
|
791
840
|
?.then((isLoggedIn: boolean) => {
|
|
792
841
|
if (!isLoggedIn) {
|
|
@@ -832,7 +881,9 @@ export class TsEmbed {
|
|
|
832
881
|
el.remove();
|
|
833
882
|
});
|
|
834
883
|
}
|
|
835
|
-
|
|
884
|
+
// Subscribe to message events only after successful
|
|
885
|
+
// auth and iframe setup
|
|
886
|
+
this.subscribeToMessageEvents();
|
|
836
887
|
})
|
|
837
888
|
.catch((error) => {
|
|
838
889
|
nextInQueue();
|
|
@@ -1140,12 +1191,12 @@ export class TsEmbed {
|
|
|
1140
1191
|
}
|
|
1141
1192
|
}
|
|
1142
1193
|
|
|
1143
|
-
|
|
1194
|
+
/**
|
|
1144
1195
|
* @hidden
|
|
1145
1196
|
* Internal state to track if the embed container is loaded.
|
|
1146
1197
|
* This is used to trigger events after the embed container is loaded.
|
|
1147
1198
|
*/
|
|
1148
|
-
|
|
1199
|
+
public isEmbedContainerLoaded = false;
|
|
1149
1200
|
|
|
1150
1201
|
/**
|
|
1151
1202
|
* @hidden
|
|
@@ -1191,7 +1242,7 @@ export class TsEmbed {
|
|
|
1191
1242
|
} else {
|
|
1192
1243
|
logger.debug('pushing callback to embedContainerReadyCallbacks', callback);
|
|
1193
1244
|
this.embedContainerReadyCallbacks.push(callback);
|
|
1194
|
-
|
|
1245
|
+
}
|
|
1195
1246
|
}
|
|
1196
1247
|
|
|
1197
1248
|
protected createEmbedContainerHandler = (source: EmbedEvent.AuthInit | EmbedEvent.EmbedListenerReady) => () => {
|
|
@@ -1220,6 +1271,7 @@ export class TsEmbed {
|
|
|
1220
1271
|
public async trigger<HostEventT extends HostEvent, PayloadT>(
|
|
1221
1272
|
messageType: HostEventT,
|
|
1222
1273
|
data: TriggerPayload<PayloadT, HostEventT> = {} as any,
|
|
1274
|
+
context: any = {},
|
|
1223
1275
|
): Promise<TriggerResponse<PayloadT, HostEventT>> {
|
|
1224
1276
|
uploadMixpanelEvent(`${MIXPANEL_EVENT.VISUAL_SDK_TRIGGER}-${messageType}`);
|
|
1225
1277
|
|
|
@@ -1232,8 +1284,18 @@ export class TsEmbed {
|
|
|
1232
1284
|
this.handleError('Host event type is undefined');
|
|
1233
1285
|
return null;
|
|
1234
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
|
+
|
|
1235
1297
|
// send an empty object, this is needed for liveboard default handlers
|
|
1236
|
-
return this.hostEventClient.triggerHostEvent(messageType, data);
|
|
1298
|
+
return this.hostEventClient.triggerHostEvent(messageType, data, context);
|
|
1237
1299
|
}
|
|
1238
1300
|
|
|
1239
1301
|
/**
|
|
@@ -1279,6 +1341,7 @@ export class TsEmbed {
|
|
|
1279
1341
|
* Creates the preRender shell
|
|
1280
1342
|
* @param showPreRenderByDefault - Show the preRender after render, hidden by default
|
|
1281
1343
|
*/
|
|
1344
|
+
|
|
1282
1345
|
public async preRender(showPreRenderByDefault = false, replaceExistingPreRender = false): Promise<TsEmbed> {
|
|
1283
1346
|
if (!this.viewConfig.preRenderId) {
|
|
1284
1347
|
logger.error(ERROR_MESSAGE.PRERENDER_ID_MISSING);
|
|
@@ -1413,8 +1476,14 @@ export class TsEmbed {
|
|
|
1413
1476
|
return this.preRender(true);
|
|
1414
1477
|
}
|
|
1415
1478
|
this.validatePreRenderViewConfig(this.viewConfig);
|
|
1479
|
+
logger.debug('triggering UpdateEmbedParams', this.viewConfig);
|
|
1480
|
+
this.executeAfterEmbedContainerLoaded(() => {
|
|
1481
|
+
this.trigger(HostEvent.UpdateEmbedParams, this.getUpdateEmbedParamsObject());
|
|
1482
|
+
});
|
|
1416
1483
|
}
|
|
1417
1484
|
|
|
1485
|
+
this.beforePrerenderVisible();
|
|
1486
|
+
|
|
1418
1487
|
if (this.el) {
|
|
1419
1488
|
this.syncPreRenderStyle();
|
|
1420
1489
|
if (!this.viewConfig.doNotTrackPreRenderSize) {
|
|
@@ -1432,8 +1501,6 @@ export class TsEmbed {
|
|
|
1432
1501
|
}
|
|
1433
1502
|
}
|
|
1434
1503
|
|
|
1435
|
-
this.beforePrerenderVisible();
|
|
1436
|
-
|
|
1437
1504
|
removeStyleProperties(this.preRenderWrapper, ['z-index', 'opacity', 'pointer-events']);
|
|
1438
1505
|
|
|
1439
1506
|
this.subscribeToEvents();
|
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 = {
|
|
@@ -6,6 +6,6 @@ describe('Exports', () => {
|
|
|
6
6
|
});
|
|
7
7
|
|
|
8
8
|
it('should not have undefined exports', () => {
|
|
9
|
-
Object.
|
|
9
|
+
Object.entries(Exports).forEach(([, exportValue]) => {expect(Boolean(exportValue)).toBe(true);});
|
|
10
10
|
});
|
|
11
11
|
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { getViewPropsAndListeners } from './util';
|
|
2
|
+
import { EmbedEvent, MessageCallback } from '../types';
|
|
3
|
+
|
|
4
|
+
describe('React util functions', () => {
|
|
5
|
+
describe('getViewPropsAndListeners', () => {
|
|
6
|
+
test('should return empty viewConfig and listeners for empty props', () => {
|
|
7
|
+
const props = {};
|
|
8
|
+
const result = getViewPropsAndListeners(props);
|
|
9
|
+
|
|
10
|
+
expect(result.viewConfig).toEqual({});
|
|
11
|
+
expect(result.listeners).toEqual({});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('should separate view config properties from props', () => {
|
|
15
|
+
const props = {
|
|
16
|
+
frameParams: { width: 100, height: 200 },
|
|
17
|
+
showLiveboardTitle: true,
|
|
18
|
+
liveboardId: 'test-liveboard-id',
|
|
19
|
+
vizId: 'test-viz-id',
|
|
20
|
+
className: 'test-class',
|
|
21
|
+
style: { color: 'red' },
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const result = getViewPropsAndListeners(props);
|
|
25
|
+
|
|
26
|
+
expect(result.viewConfig).toEqual({
|
|
27
|
+
frameParams: { width: 100, height: 200 },
|
|
28
|
+
showLiveboardTitle: true,
|
|
29
|
+
liveboardId: 'test-liveboard-id',
|
|
30
|
+
vizId: 'test-viz-id',
|
|
31
|
+
className: 'test-class',
|
|
32
|
+
style: { color: 'red' },
|
|
33
|
+
});
|
|
34
|
+
expect(result.listeners).toEqual({});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('should separate event handlers from props', () => {
|
|
38
|
+
const onInit: MessageCallback = jest.fn();
|
|
39
|
+
const onLoad: MessageCallback = jest.fn();
|
|
40
|
+
const onData: MessageCallback = jest.fn();
|
|
41
|
+
|
|
42
|
+
const props = {
|
|
43
|
+
onInit,
|
|
44
|
+
onLoad,
|
|
45
|
+
onData,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const result = getViewPropsAndListeners(props);
|
|
49
|
+
|
|
50
|
+
expect(result.viewConfig).toEqual({});
|
|
51
|
+
expect(result.listeners).toEqual({
|
|
52
|
+
[EmbedEvent.Init]: onInit,
|
|
53
|
+
[EmbedEvent.Load]: onLoad,
|
|
54
|
+
[EmbedEvent.Data]: onData,
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('should handle both view config and event handlers', () => {
|
|
59
|
+
const onInit: MessageCallback = jest.fn();
|
|
60
|
+
const onAuthInit: MessageCallback = jest.fn();
|
|
61
|
+
const onQueryChanged: MessageCallback = jest.fn();
|
|
62
|
+
|
|
63
|
+
const props = {
|
|
64
|
+
liveboardId: 'test-liveboard-id',
|
|
65
|
+
showLiveboardTitle: false,
|
|
66
|
+
frameParams: { height: 500 },
|
|
67
|
+
onInit,
|
|
68
|
+
onAuthInit,
|
|
69
|
+
onQueryChanged,
|
|
70
|
+
className: 'embed-container',
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const result = getViewPropsAndListeners(props);
|
|
74
|
+
|
|
75
|
+
expect(result.viewConfig).toEqual({
|
|
76
|
+
liveboardId: 'test-liveboard-id',
|
|
77
|
+
showLiveboardTitle: false,
|
|
78
|
+
frameParams: { height: 500 },
|
|
79
|
+
className: 'embed-container',
|
|
80
|
+
});
|
|
81
|
+
expect(result.listeners).toEqual({
|
|
82
|
+
[EmbedEvent.Init]: onInit,
|
|
83
|
+
[EmbedEvent.AuthInit]: onAuthInit,
|
|
84
|
+
[EmbedEvent.QueryChanged]: onQueryChanged,
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
package/src/react/util.ts
CHANGED
|
@@ -24,10 +24,10 @@ export function getViewPropsAndListeners<
|
|
|
24
24
|
return Object.keys(props).reduce(
|
|
25
25
|
(accu, key) => {
|
|
26
26
|
if (key.startsWith('on')) {
|
|
27
|
-
const eventName = key.substr(2);
|
|
28
|
-
accu.listeners[EmbedEvent[eventName]] = props[key];
|
|
27
|
+
const eventName = key.substr(2) as any;
|
|
28
|
+
(accu.listeners as any)[EmbedEvent[eventName as keyof typeof EmbedEvent] as any] = props[key as keyof T];
|
|
29
29
|
} else {
|
|
30
|
-
accu.viewConfig[key] = props[key];
|
|
30
|
+
(accu.viewConfig as any)[key] = props[key as keyof T];
|
|
31
31
|
}
|
|
32
32
|
return accu;
|
|
33
33
|
},
|