@thoughtspot/visual-embed-sdk 1.39.3 → 1.40.1-alpha.1
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/src/embed/app.d.ts +56 -0
- package/cjs/src/embed/app.d.ts.map +1 -1
- package/cjs/src/embed/app.js +47 -8
- package/cjs/src/embed/app.js.map +1 -1
- package/cjs/src/embed/app.spec.js +322 -7
- package/cjs/src/embed/app.spec.js.map +1 -1
- package/cjs/src/embed/liveboard.d.ts +58 -1
- package/cjs/src/embed/liveboard.d.ts.map +1 -1
- package/cjs/src/embed/liveboard.js +59 -8
- package/cjs/src/embed/liveboard.js.map +1 -1
- package/cjs/src/embed/liveboard.spec.js +206 -0
- package/cjs/src/embed/liveboard.spec.js.map +1 -1
- package/cjs/src/embed/ts-embed.d.ts +7 -0
- package/cjs/src/embed/ts-embed.d.ts.map +1 -1
- package/cjs/src/embed/ts-embed.js +61 -7
- package/cjs/src/embed/ts-embed.js.map +1 -1
- package/cjs/src/types.d.ts +37 -6
- package/cjs/src/types.d.ts.map +1 -1
- package/cjs/src/types.js +35 -4
- package/cjs/src/types.js.map +1 -1
- package/cjs/src/utils/processTrigger.js +2 -1
- package/cjs/src/utils/processTrigger.js.map +1 -1
- package/cjs/src/utils.d.ts +6 -0
- package/cjs/src/utils.d.ts.map +1 -1
- package/cjs/src/utils.js +23 -3
- package/cjs/src/utils.js.map +1 -1
- package/cjs/src/utils.spec.js +212 -1
- package/cjs/src/utils.spec.js.map +1 -1
- package/dist/{index-ZrE8YYq8.js → index-CmEQfuE3.js} +1 -1
- package/dist/index-D1pyb7RG.js +7371 -0
- package/dist/index-DeFzsyFF.js +7371 -0
- package/dist/index-Dpf0rd6w.js +7371 -0
- package/dist/index-UuEbsISo.js +7447 -0
- package/dist/index-e3Uw3YFO.js +7371 -0
- package/dist/src/embed/app.d.ts +56 -0
- package/dist/src/embed/app.d.ts.map +1 -1
- package/dist/src/embed/bodyless-conversation.d.ts +0 -4
- package/dist/src/embed/bodyless-conversation.d.ts.map +1 -1
- package/dist/src/embed/liveboard.d.ts +56 -0
- package/dist/src/embed/liveboard.d.ts.map +1 -1
- package/dist/src/react/index.d.ts +0 -2
- package/dist/src/react/index.d.ts.map +1 -1
- package/dist/src/types.d.ts +16 -198
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/graphql/nlsService/conversation-service.d.ts.map +1 -1
- package/dist/src/utils.d.ts +6 -0
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/tsembed-react.es.js +137 -224
- package/dist/tsembed-react.js +136 -223
- package/dist/tsembed.es.js +137 -224
- package/dist/tsembed.js +136 -223
- package/dist/visual-embed-sdk-react-full.d.ts +106 -204
- package/dist/visual-embed-sdk-react.d.ts +106 -204
- package/dist/visual-embed-sdk.d.ts +106 -202
- package/lib/src/embed/app.d.ts +56 -0
- package/lib/src/embed/app.d.ts.map +1 -1
- package/lib/src/embed/app.js +48 -9
- package/lib/src/embed/app.js.map +1 -1
- package/lib/src/embed/app.spec.js +322 -7
- package/lib/src/embed/app.spec.js.map +1 -1
- package/lib/src/embed/liveboard.d.ts +58 -1
- package/lib/src/embed/liveboard.d.ts.map +1 -1
- package/lib/src/embed/liveboard.js +60 -9
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/embed/liveboard.spec.js +206 -0
- package/lib/src/embed/liveboard.spec.js.map +1 -1
- package/lib/src/embed/ts-embed.d.ts +7 -0
- package/lib/src/embed/ts-embed.d.ts.map +1 -1
- package/lib/src/embed/ts-embed.js +61 -7
- package/lib/src/embed/ts-embed.js.map +1 -1
- package/lib/src/types.d.ts +37 -6
- package/lib/src/types.d.ts.map +1 -1
- package/lib/src/types.js +35 -4
- package/lib/src/types.js.map +1 -1
- package/lib/src/utils/processTrigger.js +2 -1
- package/lib/src/utils/processTrigger.js.map +1 -1
- package/lib/src/utils.d.ts +6 -0
- package/lib/src/utils.d.ts.map +1 -1
- package/lib/src/utils.js +21 -2
- package/lib/src/utils.js.map +1 -1
- package/lib/src/utils.spec.js +213 -2
- package/lib/src/utils.spec.js.map +1 -1
- package/lib/src/visual-embed-sdk.d.ts +106 -202
- package/package.json +1 -2
- package/src/embed/app.spec.ts +397 -8
- package/src/embed/app.ts +106 -12
- package/src/embed/liveboard.spec.ts +254 -1
- package/src/embed/liveboard.ts +109 -11
- package/src/embed/ts-embed.ts +84 -21
- package/src/types.ts +36 -5
- package/src/utils/processTrigger.ts +1 -1
- package/src/utils.spec.ts +250 -2
- package/src/utils.ts +28 -2
package/src/embed/ts-embed.ts
CHANGED
|
@@ -185,7 +185,10 @@ export class TsEmbed {
|
|
|
185
185
|
*/
|
|
186
186
|
private fullscreenChangeHandler: (() => void) | null = null;
|
|
187
187
|
|
|
188
|
+
public id: string;
|
|
189
|
+
|
|
188
190
|
constructor(domSelector: DOMSelector, viewConfig?: ViewConfig) {
|
|
191
|
+
this.id = Date.now().toString();
|
|
189
192
|
this.el = getDOMNode(domSelector);
|
|
190
193
|
this.eventHandlerMap = new Map();
|
|
191
194
|
this.isError = false;
|
|
@@ -237,7 +240,7 @@ export class TsEmbed {
|
|
|
237
240
|
* @param event The window message event
|
|
238
241
|
*/
|
|
239
242
|
private getEventType(event: MessageEvent) {
|
|
240
|
-
|
|
243
|
+
|
|
241
244
|
return event.data?.type || event.data?.__type;
|
|
242
245
|
}
|
|
243
246
|
|
|
@@ -281,11 +284,11 @@ export class TsEmbed {
|
|
|
281
284
|
*/
|
|
282
285
|
private isFullAppEmbedWithVisiblePrimaryNavbar(): boolean {
|
|
283
286
|
const appViewConfig = this.viewConfig as any;
|
|
284
|
-
|
|
287
|
+
|
|
285
288
|
// Check if this is a FullAppEmbed (AppEmbed)
|
|
286
289
|
// showPrimaryNavbar defaults to true if not explicitly set to false
|
|
287
290
|
return (
|
|
288
|
-
appViewConfig.embedComponentType === 'AppEmbed'
|
|
291
|
+
appViewConfig.embedComponentType === 'AppEmbed'
|
|
289
292
|
&& appViewConfig.showPrimaryNavbar === true
|
|
290
293
|
);
|
|
291
294
|
}
|
|
@@ -310,6 +313,8 @@ export class TsEmbed {
|
|
|
310
313
|
|
|
311
314
|
private subscribedListeners: Record<string, any> = {};
|
|
312
315
|
|
|
316
|
+
public isEmbedContainerLoaded = false;
|
|
317
|
+
|
|
313
318
|
/**
|
|
314
319
|
* Adds a global event listener to window for "message" events.
|
|
315
320
|
* ThoughtSpot detects if a particular event is targeted to this
|
|
@@ -431,7 +436,8 @@ export class TsEmbed {
|
|
|
431
436
|
private updateAuthToken = async (_: any, responder: any) => {
|
|
432
437
|
const { authType } = this.embedConfig;
|
|
433
438
|
let { autoLogin } = this.embedConfig;
|
|
434
|
-
// Default autoLogin: true for cookieless if undefined/null, otherwise
|
|
439
|
+
// Default autoLogin: true for cookieless if undefined/null, otherwise
|
|
440
|
+
// false
|
|
435
441
|
autoLogin = autoLogin ?? (authType === AuthType.TrustedAuthTokenCookieless);
|
|
436
442
|
if (autoLogin && authType === AuthType.TrustedAuthTokenCookieless) {
|
|
437
443
|
try {
|
|
@@ -474,11 +480,65 @@ export class TsEmbed {
|
|
|
474
480
|
notifyAuthFailure(AuthFailureType.IDLE_SESSION_TIMEOUT);
|
|
475
481
|
};
|
|
476
482
|
|
|
483
|
+
private pendingEvents: Array<{ eventType: HostEvent, data: TriggerPayload<any, HostEvent>, onEventTriggered?: () => void }> = [];
|
|
484
|
+
|
|
485
|
+
protected getPreRenderObj<T extends TsEmbed>() {
|
|
486
|
+
const embedObj = (this.insertedDomEl as any)?.[this.embedNodeKey] as T;
|
|
487
|
+
if (embedObj === (this as any)) {
|
|
488
|
+
console.log('embedObj is same as this');
|
|
489
|
+
return null;
|
|
490
|
+
}
|
|
491
|
+
return (this.insertedDomEl as any)?.[this.embedNodeKey] as T;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
private checkEmbedContainerLoaded() {
|
|
495
|
+
if (this.isEmbedContainerLoaded) return true;
|
|
496
|
+
|
|
497
|
+
const preRenderObj = this.getPreRenderObj<TsEmbed>();
|
|
498
|
+
if (preRenderObj && preRenderObj.isEmbedContainerLoaded) {
|
|
499
|
+
this.isEmbedContainerLoaded = true;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
console.log('checkEmbedContainerLoaded', this.isEmbedContainerLoaded);
|
|
503
|
+
|
|
504
|
+
return this.isEmbedContainerLoaded;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
private executePendingEvents() {
|
|
508
|
+
console.log('executePendingEvents', this.pendingEvents);
|
|
509
|
+
setTimeout(() => {
|
|
510
|
+
this.pendingEvents.forEach((event) => {
|
|
511
|
+
console.log('executing event', event.eventType, event.data);
|
|
512
|
+
this.trigger(event.eventType, event.data);
|
|
513
|
+
event.onEventTriggered?.();
|
|
514
|
+
});
|
|
515
|
+
this.pendingEvents = [];
|
|
516
|
+
}, 1000);
|
|
517
|
+
}
|
|
518
|
+
protected triggerAfterLoad(eventType: HostEvent, data: TriggerPayload<any, HostEvent>, onEventTriggered?: () => void) {
|
|
519
|
+
if (this.checkEmbedContainerLoaded()) {
|
|
520
|
+
console.log('triggerAfterLoad', eventType, data);
|
|
521
|
+
this.trigger(eventType, data);
|
|
522
|
+
onEventTriggered?.();
|
|
523
|
+
} else {
|
|
524
|
+
console.log('pushing to pendingEvents', eventType, data, this.getPreRenderObj());
|
|
525
|
+
this.pendingEvents.push({ eventType, data, onEventTriggered });
|
|
526
|
+
console.log('pendingEvents', this.pendingEvents);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
477
530
|
/**
|
|
478
531
|
* Register APP_INIT event and sendback init payload
|
|
479
532
|
*/
|
|
480
533
|
private registerAppInit = () => {
|
|
481
534
|
this.on(EmbedEvent.APP_INIT, this.appInitCb, { start: false }, true);
|
|
535
|
+
this.on(EmbedEvent.AuthInit, () => {
|
|
536
|
+
console.log('AuthInit', this.getPreRenderObj());
|
|
537
|
+
this.isEmbedContainerLoaded = true;
|
|
538
|
+
console.log('isEmbedContainerLoaded', this.isEmbedContainerLoaded);
|
|
539
|
+
console.log('executePendingEvents', this.pendingEvents);
|
|
540
|
+
this.executePendingEvents();
|
|
541
|
+
}, { start: false }, true);
|
|
482
542
|
this.on(EmbedEvent.AuthExpire, this.updateAuthToken, { start: false }, true);
|
|
483
543
|
this.on(EmbedEvent.IdleSessionTimeout, this.idleSessionTimeout, { start: false }, true);
|
|
484
544
|
};
|
|
@@ -798,8 +858,9 @@ export class TsEmbed {
|
|
|
798
858
|
}
|
|
799
859
|
});
|
|
800
860
|
}
|
|
801
|
-
|
|
802
|
-
// Setup fullscreen change handler after iframe is
|
|
861
|
+
|
|
862
|
+
// Setup fullscreen change handler after iframe is
|
|
863
|
+
// loaded and ready
|
|
803
864
|
this.setupFullscreenChangeHandler();
|
|
804
865
|
});
|
|
805
866
|
this.iFrame.addEventListener('error', () => {
|
|
@@ -926,7 +987,7 @@ export class TsEmbed {
|
|
|
926
987
|
const div = document.createElement('div');
|
|
927
988
|
div.innerHTML = child;
|
|
928
989
|
div.id = TS_EMBED_ID;
|
|
929
|
-
|
|
990
|
+
|
|
930
991
|
child = div;
|
|
931
992
|
}
|
|
932
993
|
if (this.el.nextElementSibling?.id === TS_EMBED_ID) {
|
|
@@ -1070,11 +1131,11 @@ export class TsEmbed {
|
|
|
1070
1131
|
if (this.isRendered) {
|
|
1071
1132
|
logger.warn('Please register event handlers before calling render');
|
|
1072
1133
|
}
|
|
1073
|
-
|
|
1134
|
+
|
|
1074
1135
|
const callbacks = this.eventHandlerMap.get(messageType) || [];
|
|
1075
1136
|
callbacks.push({ options, callback });
|
|
1076
1137
|
this.eventHandlerMap.set(messageType, callbacks);
|
|
1077
|
-
|
|
1138
|
+
|
|
1078
1139
|
return this;
|
|
1079
1140
|
}
|
|
1080
1141
|
|
|
@@ -1174,7 +1235,7 @@ export class TsEmbed {
|
|
|
1174
1235
|
}
|
|
1175
1236
|
await this.isReadyForRenderPromise;
|
|
1176
1237
|
this.isRendered = true;
|
|
1177
|
-
|
|
1238
|
+
|
|
1178
1239
|
return this;
|
|
1179
1240
|
}
|
|
1180
1241
|
|
|
@@ -1288,11 +1349,11 @@ export class TsEmbed {
|
|
|
1288
1349
|
) {
|
|
1289
1350
|
logger.warn(
|
|
1290
1351
|
`${viewConfig.embedComponentType || 'Component'} was pre-rendered with `
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1352
|
+
+ `"${key}" as "${JSON.stringify(preRenderedObject.viewConfig[key])}" `
|
|
1353
|
+
+ `but a different value "${JSON.stringify(viewConfig[key])}" `
|
|
1354
|
+
+ 'was passed to the Embed component. '
|
|
1355
|
+
+ 'The new value provided is ignored, the value provided during '
|
|
1356
|
+
+ 'preRender is used.',
|
|
1296
1357
|
);
|
|
1297
1358
|
}
|
|
1298
1359
|
});
|
|
@@ -1318,8 +1379,11 @@ export class TsEmbed {
|
|
|
1318
1379
|
return this.preRender(true);
|
|
1319
1380
|
}
|
|
1320
1381
|
this.validatePreRenderViewConfig(this.viewConfig);
|
|
1382
|
+
this.trigger(HostEvent.UpdateEmbedParams, this.viewConfig);
|
|
1321
1383
|
}
|
|
1322
1384
|
|
|
1385
|
+
this.beforePrerenderVisible();
|
|
1386
|
+
|
|
1323
1387
|
if (this.el) {
|
|
1324
1388
|
this.syncPreRenderStyle();
|
|
1325
1389
|
if (!this.viewConfig.doNotTrackPreRenderSize) {
|
|
@@ -1337,12 +1401,10 @@ export class TsEmbed {
|
|
|
1337
1401
|
}
|
|
1338
1402
|
}
|
|
1339
1403
|
|
|
1340
|
-
this.beforePrerenderVisible();
|
|
1341
|
-
|
|
1342
1404
|
removeStyleProperties(this.preRenderWrapper, ['z-index', 'opacity', 'pointer-events']);
|
|
1343
1405
|
|
|
1344
1406
|
this.subscribeToEvents();
|
|
1345
|
-
|
|
1407
|
+
|
|
1346
1408
|
// Setup fullscreen change handler for prerendered components
|
|
1347
1409
|
if (this.iFrame) {
|
|
1348
1410
|
this.setupFullscreenChangeHandler();
|
|
@@ -1431,7 +1493,7 @@ export class TsEmbed {
|
|
|
1431
1493
|
private setupFullscreenChangeHandler() {
|
|
1432
1494
|
const embedConfig = getEmbedConfig();
|
|
1433
1495
|
const disableFullscreenPresentation = embedConfig?.disableFullscreenPresentation ?? true;
|
|
1434
|
-
|
|
1496
|
+
|
|
1435
1497
|
if (disableFullscreenPresentation) {
|
|
1436
1498
|
return;
|
|
1437
1499
|
}
|
|
@@ -1444,7 +1506,8 @@ export class TsEmbed {
|
|
|
1444
1506
|
const isFullscreen = !!document.fullscreenElement;
|
|
1445
1507
|
if (!isFullscreen) {
|
|
1446
1508
|
logger.info('Exited fullscreen mode - triggering ExitPresentMode');
|
|
1447
|
-
// Only trigger if iframe is available and contentWindow is
|
|
1509
|
+
// Only trigger if iframe is available and contentWindow is
|
|
1510
|
+
// accessible
|
|
1448
1511
|
if (this.iFrame && this.iFrame.contentWindow) {
|
|
1449
1512
|
this.trigger(HostEvent.ExitPresentMode);
|
|
1450
1513
|
} else {
|
|
@@ -1540,6 +1603,6 @@ export class V1Embed extends TsEmbed {
|
|
|
1540
1603
|
* Only for testing purposes.
|
|
1541
1604
|
* @hidden
|
|
1542
1605
|
*/
|
|
1543
|
-
|
|
1606
|
+
|
|
1544
1607
|
public test__executeCallbacks = this.executeCallbacks;
|
|
1545
1608
|
}
|
package/src/types.ts
CHANGED
|
@@ -2707,6 +2707,12 @@ export enum EmbedEvent {
|
|
|
2707
2707
|
* @version SDK : 1.40.0 | ThoughtSpot: 10.11.0.cl
|
|
2708
2708
|
*/
|
|
2709
2709
|
ExitPresentMode = 'exitPresentMode',
|
|
2710
|
+
/**
|
|
2711
|
+
* Emitted when a user requests the full height lazy load data.
|
|
2712
|
+
* @version SDK : 1.39.0 | ThoughtSpot : 10.10.0.cl
|
|
2713
|
+
* @hidden
|
|
2714
|
+
*/
|
|
2715
|
+
RequestVisibleEmbedCoordinates = 'requestVisibleEmbedCoordinates',
|
|
2710
2716
|
/**
|
|
2711
2717
|
* Emitted when spotter response is text data
|
|
2712
2718
|
* @example
|
|
@@ -2802,7 +2808,7 @@ export enum EmbedEvent {
|
|
|
2802
2808
|
* // create the liveboard embed.
|
|
2803
2809
|
*
|
|
2804
2810
|
* liveboardEmbed.trigger(HostEvent.UpdateRuntimeFilters, [
|
|
2805
|
-
* { columnName: 'state, operator: RuntimeFilterOp.EQ, values: [
|
|
2811
|
+
* { columnName: 'state', operator: RuntimeFilterOp.EQ, values: ["california"]}
|
|
2806
2812
|
* ]);
|
|
2807
2813
|
* ```
|
|
2808
2814
|
* @example
|
|
@@ -3011,12 +3017,12 @@ export enum HostEvent {
|
|
|
3011
3017
|
* Works with Search and Liveboard embed.
|
|
3012
3018
|
* @param - { columnId: string,
|
|
3013
3019
|
* name: string,
|
|
3014
|
-
* type:
|
|
3015
|
-
* dataType:
|
|
3020
|
+
* type: ATTRIBUTE/MEASURE,
|
|
3021
|
+
* dataType: INT64/CHAR/DATE }
|
|
3016
3022
|
* @example
|
|
3017
3023
|
* ```js
|
|
3018
3024
|
* searchEmbed.trigger(HostEvent.OpenFilter,
|
|
3019
|
-
* {column: { columnId: '<column-GUID>', name: 'column name', type: '
|
|
3025
|
+
* {column: { columnId: '<column-GUID>', name: 'column name', type: 'ATTRIBUTE', dataType: 'INT64'}})
|
|
3020
3026
|
* ```
|
|
3021
3027
|
* @example
|
|
3022
3028
|
* ```js
|
|
@@ -3984,7 +3990,19 @@ export enum HostEvent {
|
|
|
3984
3990
|
* @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl
|
|
3985
3991
|
*/
|
|
3986
3992
|
ExitPresentMode = 'exitPresentMode',
|
|
3987
|
-
|
|
3993
|
+
/**
|
|
3994
|
+
* Triggers the full height lazy load data.
|
|
3995
|
+
* @example
|
|
3996
|
+
* ```js
|
|
3997
|
+
* liveboardEmbed.on(EmbedEvent.RequestVisibleEmbedCoordinates, (payload) => {
|
|
3998
|
+
* console.log(payload);
|
|
3999
|
+
* });
|
|
4000
|
+
* ```
|
|
4001
|
+
* @version SDK: 1.39.0 | ThoughtSpot: 10.10.0.cl
|
|
4002
|
+
*
|
|
4003
|
+
* @hidden
|
|
4004
|
+
*/
|
|
4005
|
+
VisibleEmbedCoordinates = 'visibleEmbedCoordinates',
|
|
3988
4006
|
/**
|
|
3989
4007
|
* Trigger the *Ask Sage* action for visualizations
|
|
3990
4008
|
* @example
|
|
@@ -3995,6 +4013,17 @@ export enum HostEvent {
|
|
|
3995
4013
|
* @version SDK: 1.41.0 | ThoughtSpot: 10.12.0.cl
|
|
3996
4014
|
*/
|
|
3997
4015
|
AskSpotter = 'askSpotter',
|
|
4016
|
+
|
|
4017
|
+
/**
|
|
4018
|
+
* @hidden
|
|
4019
|
+
* Triggers the update of the embed params.
|
|
4020
|
+
*
|
|
4021
|
+
* @example
|
|
4022
|
+
* ```js
|
|
4023
|
+
* liveboardEmbed.trigger(HostEvent.UpdateEmbedParams, viewConfig);
|
|
4024
|
+
* ```
|
|
4025
|
+
*/
|
|
4026
|
+
UpdateEmbedParams = 'updateEmbedParams',
|
|
3998
4027
|
}
|
|
3999
4028
|
|
|
4000
4029
|
/**
|
|
@@ -4139,6 +4168,8 @@ export enum Param {
|
|
|
4139
4168
|
PrimaryAction = 'primaryAction',
|
|
4140
4169
|
isSpotterAgentEmbed = 'isSpotterAgentEmbed',
|
|
4141
4170
|
IsLiveboardStylingAndGroupingEnabled = 'isLiveboardStylingAndGroupingEnabled',
|
|
4171
|
+
IsLazyLoadingForEmbedEnabled = 'isLazyLoadingForEmbedEnabled',
|
|
4172
|
+
RootMarginForLazyLoad = 'rootMarginForLazyLoad'
|
|
4142
4173
|
}
|
|
4143
4174
|
|
|
4144
4175
|
/**
|
|
@@ -31,7 +31,7 @@ function postIframeMessage(
|
|
|
31
31
|
thoughtSpotHost: string,
|
|
32
32
|
channel?: MessageChannel,
|
|
33
33
|
) {
|
|
34
|
-
return iFrame.contentWindow
|
|
34
|
+
return iFrame.contentWindow?.postMessage(message, thoughtSpotHost, [channel?.port2]);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export const TRIGGER_TIMEOUT = 30000;
|
package/src/utils.spec.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
handlePresentEvent,
|
|
17
17
|
handleExitPresentMode,
|
|
18
18
|
getTypeFromValue,
|
|
19
|
+
calculateVisibleElementData,
|
|
19
20
|
} from './utils';
|
|
20
21
|
import { RuntimeFilterOp } from './types';
|
|
21
22
|
import { logger } from './utils/logger';
|
|
@@ -315,7 +316,7 @@ describe('unit test for utils', () => {
|
|
|
315
316
|
});
|
|
316
317
|
|
|
317
318
|
test('Object should be set if not', () => {
|
|
318
|
-
|
|
319
|
+
|
|
319
320
|
(window as any)._tsEmbedSDK = null;
|
|
320
321
|
|
|
321
322
|
storeValueInWindow('test', 'testValue');
|
|
@@ -341,7 +342,7 @@ describe('Fullscreen Utility Functions', () => {
|
|
|
341
342
|
|
|
342
343
|
beforeEach(() => {
|
|
343
344
|
jest.clearAllMocks();
|
|
344
|
-
|
|
345
|
+
|
|
345
346
|
// Store and mock exitFullscreen
|
|
346
347
|
originalExitFullscreen = document.exitFullscreen;
|
|
347
348
|
document.exitFullscreen = jest.fn();
|
|
@@ -436,3 +437,250 @@ describe('Fullscreen Utility Functions', () => {
|
|
|
436
437
|
});
|
|
437
438
|
});
|
|
438
439
|
});
|
|
440
|
+
|
|
441
|
+
describe('calculateVisibleElementData', () => {
|
|
442
|
+
let mockElement: HTMLElement;
|
|
443
|
+
let originalInnerHeight: number;
|
|
444
|
+
let originalInnerWidth: number;
|
|
445
|
+
|
|
446
|
+
beforeEach(() => {
|
|
447
|
+
// Store original window dimensions
|
|
448
|
+
originalInnerHeight = window.innerHeight;
|
|
449
|
+
originalInnerWidth = window.innerWidth;
|
|
450
|
+
|
|
451
|
+
// Mock window dimensions
|
|
452
|
+
Object.defineProperty(window, 'innerHeight', {
|
|
453
|
+
writable: true,
|
|
454
|
+
configurable: true,
|
|
455
|
+
value: 800,
|
|
456
|
+
});
|
|
457
|
+
Object.defineProperty(window, 'innerWidth', {
|
|
458
|
+
writable: true,
|
|
459
|
+
configurable: true,
|
|
460
|
+
value: 1200,
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// Create mock element
|
|
464
|
+
mockElement = document.createElement('div');
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
afterEach(() => {
|
|
468
|
+
// Restore original window dimensions
|
|
469
|
+
Object.defineProperty(window, 'innerHeight', {
|
|
470
|
+
value: originalInnerHeight,
|
|
471
|
+
});
|
|
472
|
+
Object.defineProperty(window, 'innerWidth', {
|
|
473
|
+
value: originalInnerWidth,
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it('should calculate data for fully visible element', () => {
|
|
478
|
+
// Mock getBoundingClientRect for element fully within viewport
|
|
479
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
480
|
+
top: 100,
|
|
481
|
+
left: 150,
|
|
482
|
+
bottom: 300,
|
|
483
|
+
right: 400,
|
|
484
|
+
width: 250,
|
|
485
|
+
height: 200,
|
|
486
|
+
} as DOMRect);
|
|
487
|
+
|
|
488
|
+
const result = calculateVisibleElementData(mockElement);
|
|
489
|
+
|
|
490
|
+
expect(result).toEqual({
|
|
491
|
+
top: 0, // Not clipped from top
|
|
492
|
+
height: 200, // Full height visible
|
|
493
|
+
left: 0, // Not clipped from left
|
|
494
|
+
width: 250, // Full width visible
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it('should calculate data for element clipped from top', () => {
|
|
499
|
+
// Mock getBoundingClientRect for element partially above viewport
|
|
500
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
501
|
+
top: -50,
|
|
502
|
+
left: 100,
|
|
503
|
+
bottom: 150,
|
|
504
|
+
right: 400,
|
|
505
|
+
width: 300,
|
|
506
|
+
height: 200,
|
|
507
|
+
} as DOMRect);
|
|
508
|
+
|
|
509
|
+
const result = calculateVisibleElementData(mockElement);
|
|
510
|
+
|
|
511
|
+
expect(result).toEqual({
|
|
512
|
+
top: 50, // Clipped 50px from top
|
|
513
|
+
height: 150, // 150px visible height (0 to 150)
|
|
514
|
+
left: 0, // Not clipped from left
|
|
515
|
+
width: 300, // Full width visible
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it('should calculate data for element clipped from left', () => {
|
|
520
|
+
// Mock getBoundingClientRect for element partially left of viewport
|
|
521
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
522
|
+
top: 100,
|
|
523
|
+
left: -80,
|
|
524
|
+
bottom: 300,
|
|
525
|
+
right: 200,
|
|
526
|
+
width: 280,
|
|
527
|
+
height: 200,
|
|
528
|
+
} as DOMRect);
|
|
529
|
+
|
|
530
|
+
const result = calculateVisibleElementData(mockElement);
|
|
531
|
+
|
|
532
|
+
expect(result).toEqual({
|
|
533
|
+
top: 0, // Not clipped from top
|
|
534
|
+
height: 200, // Full height visible
|
|
535
|
+
left: 80, // Clipped 80px from left
|
|
536
|
+
width: 200, // 200px visible width (0 to 200)
|
|
537
|
+
});
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
it('should calculate data for element clipped from bottom', () => {
|
|
541
|
+
// Mock getBoundingClientRect for element extending below viewport
|
|
542
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
543
|
+
top: 600,
|
|
544
|
+
left: 100,
|
|
545
|
+
bottom: 950, // Extends beyond window height of 800
|
|
546
|
+
right: 400,
|
|
547
|
+
width: 300,
|
|
548
|
+
height: 350,
|
|
549
|
+
} as DOMRect);
|
|
550
|
+
|
|
551
|
+
const result = calculateVisibleElementData(mockElement);
|
|
552
|
+
|
|
553
|
+
expect(result).toEqual({
|
|
554
|
+
top: 0, // Not clipped from top
|
|
555
|
+
height: 200, // Only 200px visible (600 to 800)
|
|
556
|
+
left: 0, // Not clipped from left
|
|
557
|
+
width: 300, // Full width visible
|
|
558
|
+
});
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it('should calculate data for element clipped from right', () => {
|
|
562
|
+
// Mock getBoundingClientRect for element extending beyond right edge
|
|
563
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
564
|
+
top: 100,
|
|
565
|
+
left: 1000,
|
|
566
|
+
bottom: 300,
|
|
567
|
+
right: 1400, // Extends beyond window width of 1200
|
|
568
|
+
width: 400,
|
|
569
|
+
height: 200,
|
|
570
|
+
} as DOMRect);
|
|
571
|
+
|
|
572
|
+
const result = calculateVisibleElementData(mockElement);
|
|
573
|
+
|
|
574
|
+
expect(result).toEqual({
|
|
575
|
+
top: 0, // Not clipped from top
|
|
576
|
+
height: 200, // Full height visible
|
|
577
|
+
left: 0, // Not clipped from left
|
|
578
|
+
width: 200, // Only 200px visible width (1000 to 1200)
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
it('should calculate data for element clipped from multiple sides', () => {
|
|
583
|
+
// Mock getBoundingClientRect for element clipped from top and left
|
|
584
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
585
|
+
top: -100,
|
|
586
|
+
left: -50,
|
|
587
|
+
bottom: 200,
|
|
588
|
+
right: 300,
|
|
589
|
+
width: 350,
|
|
590
|
+
height: 300,
|
|
591
|
+
} as DOMRect);
|
|
592
|
+
|
|
593
|
+
const result = calculateVisibleElementData(mockElement);
|
|
594
|
+
|
|
595
|
+
expect(result).toEqual({
|
|
596
|
+
top: 100, // Clipped 100px from top
|
|
597
|
+
height: 200, // 200px visible height (0 to 200)
|
|
598
|
+
left: 50, // Clipped 50px from left
|
|
599
|
+
width: 300, // 300px visible width (0 to 300)
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
it('should handle element completely outside viewport (above)', () => {
|
|
604
|
+
// Mock getBoundingClientRect for element completely above viewport
|
|
605
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
606
|
+
top: -300,
|
|
607
|
+
left: 100,
|
|
608
|
+
bottom: -100,
|
|
609
|
+
right: 400,
|
|
610
|
+
width: 300,
|
|
611
|
+
height: 200,
|
|
612
|
+
} as DOMRect);
|
|
613
|
+
|
|
614
|
+
const result = calculateVisibleElementData(mockElement);
|
|
615
|
+
|
|
616
|
+
expect(result).toEqual({
|
|
617
|
+
top: 300, // Clipped 300px from top
|
|
618
|
+
height: 0, // No visible height (clamped from negative)
|
|
619
|
+
left: 0, // Not clipped from left
|
|
620
|
+
width: 300, // Full width would be visible if in viewport
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
it('should handle element completely outside viewport (left)', () => {
|
|
625
|
+
// Mock getBoundingClientRect for element completely left of viewport
|
|
626
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
627
|
+
top: 100,
|
|
628
|
+
left: -400,
|
|
629
|
+
bottom: 300,
|
|
630
|
+
right: -100,
|
|
631
|
+
width: 300,
|
|
632
|
+
height: 200,
|
|
633
|
+
} as DOMRect);
|
|
634
|
+
|
|
635
|
+
const result = calculateVisibleElementData(mockElement);
|
|
636
|
+
|
|
637
|
+
expect(result).toEqual({
|
|
638
|
+
top: 0, // Not clipped from top
|
|
639
|
+
height: 200, // Full height would be visible if in viewport
|
|
640
|
+
left: 400, // Clipped 400px from left
|
|
641
|
+
width: 0, // No visible width (min(1200, -100) - max(-400, 0) = -100 - 0 = -100, but clamped)
|
|
642
|
+
});
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
it('should handle element larger than viewport', () => {
|
|
646
|
+
// Mock getBoundingClientRect for element larger than viewport
|
|
647
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
648
|
+
top: -200,
|
|
649
|
+
left: -300,
|
|
650
|
+
bottom: 1000,
|
|
651
|
+
right: 1500,
|
|
652
|
+
width: 1800,
|
|
653
|
+
height: 1200,
|
|
654
|
+
} as DOMRect);
|
|
655
|
+
|
|
656
|
+
const result = calculateVisibleElementData(mockElement);
|
|
657
|
+
|
|
658
|
+
expect(result).toEqual({
|
|
659
|
+
top: 200, // Clipped 200px from top
|
|
660
|
+
height: 800, // Visible height equals window height
|
|
661
|
+
left: 300, // Clipped 300px from left
|
|
662
|
+
width: 1200, // Visible width equals window width
|
|
663
|
+
});
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
it('should handle element exactly at viewport boundaries', () => {
|
|
667
|
+
// Mock getBoundingClientRect for element at exact viewport boundaries
|
|
668
|
+
jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
|
|
669
|
+
top: 0,
|
|
670
|
+
left: 0,
|
|
671
|
+
bottom: 800,
|
|
672
|
+
right: 1200,
|
|
673
|
+
width: 1200,
|
|
674
|
+
height: 800,
|
|
675
|
+
} as DOMRect);
|
|
676
|
+
|
|
677
|
+
const result = calculateVisibleElementData(mockElement);
|
|
678
|
+
|
|
679
|
+
expect(result).toEqual({
|
|
680
|
+
top: 0, // Not clipped from top
|
|
681
|
+
height: 800, // Full viewport height
|
|
682
|
+
left: 0, // Not clipped from left
|
|
683
|
+
width: 1200, // Full viewport width
|
|
684
|
+
});
|
|
685
|
+
});
|
|
686
|
+
});
|
package/src/utils.ts
CHANGED
|
@@ -269,7 +269,7 @@ export const getOperationNameFromQuery = (query: string) => {
|
|
|
269
269
|
export function removeTypename(obj: any) {
|
|
270
270
|
if (!obj || typeof obj !== 'object') return obj;
|
|
271
271
|
|
|
272
|
-
|
|
272
|
+
|
|
273
273
|
for (const key in obj) {
|
|
274
274
|
if (key === '__typename') {
|
|
275
275
|
delete obj[key];
|
|
@@ -300,7 +300,11 @@ export const setStyleProperties = (
|
|
|
300
300
|
): void => {
|
|
301
301
|
if (!element?.style) return;
|
|
302
302
|
Object.keys(styleProperties).forEach((styleProperty) => {
|
|
303
|
-
|
|
303
|
+
const styleKey = styleProperty as keyof CSSStyleDeclaration;
|
|
304
|
+
const value = styleProperties[styleKey];
|
|
305
|
+
if (value !== undefined) {
|
|
306
|
+
(element.style as any)[styleKey] = value.toString();
|
|
307
|
+
}
|
|
304
308
|
});
|
|
305
309
|
};
|
|
306
310
|
/**
|
|
@@ -463,3 +467,25 @@ export const handleExitPresentMode = async (): Promise<void> => {
|
|
|
463
467
|
|
|
464
468
|
logger.warn('Exit fullscreen API is not supported by this browser.');
|
|
465
469
|
};
|
|
470
|
+
|
|
471
|
+
export const calculateVisibleElementData = (element: HTMLElement) => {
|
|
472
|
+
const rect = element.getBoundingClientRect();
|
|
473
|
+
|
|
474
|
+
const windowHeight = window.innerHeight;
|
|
475
|
+
const windowWidth = window.innerWidth;
|
|
476
|
+
|
|
477
|
+
const frameRelativeTop = Math.max(rect.top, 0);
|
|
478
|
+
const frameRelativeLeft = Math.max(rect.left, 0);
|
|
479
|
+
|
|
480
|
+
const frameRelativeBottom = Math.min(windowHeight, rect.bottom);
|
|
481
|
+
const frameRelativeRight = Math.min(windowWidth, rect.right);
|
|
482
|
+
|
|
483
|
+
const data = {
|
|
484
|
+
top: Math.max(0, rect.top * -1),
|
|
485
|
+
height: Math.max(0, frameRelativeBottom - frameRelativeTop),
|
|
486
|
+
left: Math.max(0, rect.left * -1),
|
|
487
|
+
width: Math.max(0, frameRelativeRight - frameRelativeLeft),
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
return data;
|
|
491
|
+
}
|