@thoughtspot/visual-embed-sdk 1.24.0-preRender.2 → 1.24.0-preRender.3

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 (129) hide show
  1. package/cjs/package.json +1 -1
  2. package/cjs/src/embed/app.d.ts +1 -3
  3. package/cjs/src/embed/app.d.ts.map +1 -1
  4. package/cjs/src/embed/app.js +2 -8
  5. package/cjs/src/embed/app.js.map +1 -1
  6. package/cjs/src/embed/liveboard.d.ts +1 -3
  7. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  8. package/cjs/src/embed/liveboard.js +2 -8
  9. package/cjs/src/embed/liveboard.js.map +1 -1
  10. package/cjs/src/embed/sage.d.ts +1 -3
  11. package/cjs/src/embed/sage.d.ts.map +1 -1
  12. package/cjs/src/embed/sage.js +2 -8
  13. package/cjs/src/embed/sage.js.map +1 -1
  14. package/cjs/src/embed/search.d.ts +1 -4
  15. package/cjs/src/embed/search.d.ts.map +1 -1
  16. package/cjs/src/embed/search.js +2 -9
  17. package/cjs/src/embed/search.js.map +1 -1
  18. package/cjs/src/embed/ts-embed.d.ts +45 -15
  19. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  20. package/cjs/src/embed/ts-embed.js +112 -92
  21. package/cjs/src/embed/ts-embed.js.map +1 -1
  22. package/cjs/src/embed/ts-embed.spec.js +102 -0
  23. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  24. package/cjs/src/react/all-types-export.d.ts +1 -1
  25. package/cjs/src/react/all-types-export.d.ts.map +1 -1
  26. package/cjs/src/react/all-types-export.js +6 -2
  27. package/cjs/src/react/all-types-export.js.map +1 -1
  28. package/cjs/src/react/index.d.ts +103 -5
  29. package/cjs/src/react/index.d.ts.map +1 -1
  30. package/cjs/src/react/index.js +81 -1
  31. package/cjs/src/react/index.js.map +1 -1
  32. package/cjs/src/react/index.spec.js +22 -0
  33. package/cjs/src/react/index.spec.js.map +1 -1
  34. package/cjs/src/types.d.ts +18 -0
  35. package/cjs/src/types.d.ts.map +1 -1
  36. package/cjs/src/types.js.map +1 -1
  37. package/cjs/src/utils.d.ts +27 -0
  38. package/cjs/src/utils.d.ts.map +1 -1
  39. package/cjs/src/utils.js +31 -0
  40. package/cjs/src/utils.js.map +1 -1
  41. package/cjs/src/utils.spec.d.ts +0 -3
  42. package/cjs/src/utils.spec.d.ts.map +1 -1
  43. package/cjs/src/utils.spec.js +59 -6
  44. package/cjs/src/utils.spec.js.map +1 -1
  45. package/dist/src/embed/app.d.ts +1 -3
  46. package/dist/src/embed/app.d.ts.map +1 -1
  47. package/dist/src/embed/liveboard.d.ts +1 -3
  48. package/dist/src/embed/liveboard.d.ts.map +1 -1
  49. package/dist/src/embed/sage.d.ts +1 -3
  50. package/dist/src/embed/sage.d.ts.map +1 -1
  51. package/dist/src/embed/search.d.ts +1 -4
  52. package/dist/src/embed/search.d.ts.map +1 -1
  53. package/dist/src/embed/ts-embed.d.ts +45 -15
  54. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  55. package/dist/src/react/all-types-export.d.ts +1 -1
  56. package/dist/src/react/all-types-export.d.ts.map +1 -1
  57. package/dist/src/react/index.d.ts +103 -5
  58. package/dist/src/react/index.d.ts.map +1 -1
  59. package/dist/src/types.d.ts +18 -0
  60. package/dist/src/types.d.ts.map +1 -1
  61. package/dist/src/utils.d.ts +27 -0
  62. package/dist/src/utils.d.ts.map +1 -1
  63. package/dist/src/utils.spec.d.ts +0 -3
  64. package/dist/src/utils.spec.d.ts.map +1 -1
  65. package/dist/tsembed-react.es.js +233 -127
  66. package/dist/tsembed-react.js +233 -126
  67. package/dist/tsembed.es.js +152 -126
  68. package/dist/tsembed.js +152 -126
  69. package/dist/visual-embed-sdk-react-full.d.ts +164 -32
  70. package/dist/visual-embed-sdk-react.d.ts +164 -32
  71. package/dist/visual-embed-sdk.d.ts +61 -27
  72. package/lib/package.json +1 -1
  73. package/lib/src/embed/app.d.ts +1 -3
  74. package/lib/src/embed/app.d.ts.map +1 -1
  75. package/lib/src/embed/app.js +2 -8
  76. package/lib/src/embed/app.js.map +1 -1
  77. package/lib/src/embed/liveboard.d.ts +1 -3
  78. package/lib/src/embed/liveboard.d.ts.map +1 -1
  79. package/lib/src/embed/liveboard.js +2 -8
  80. package/lib/src/embed/liveboard.js.map +1 -1
  81. package/lib/src/embed/sage.d.ts +1 -3
  82. package/lib/src/embed/sage.d.ts.map +1 -1
  83. package/lib/src/embed/sage.js +2 -8
  84. package/lib/src/embed/sage.js.map +1 -1
  85. package/lib/src/embed/search.d.ts +1 -4
  86. package/lib/src/embed/search.d.ts.map +1 -1
  87. package/lib/src/embed/search.js +2 -9
  88. package/lib/src/embed/search.js.map +1 -1
  89. package/lib/src/embed/ts-embed.d.ts +45 -15
  90. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  91. package/lib/src/embed/ts-embed.js +112 -92
  92. package/lib/src/embed/ts-embed.js.map +1 -1
  93. package/lib/src/embed/ts-embed.spec.js +102 -0
  94. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  95. package/lib/src/react/all-types-export.d.ts +1 -1
  96. package/lib/src/react/all-types-export.d.ts.map +1 -1
  97. package/lib/src/react/all-types-export.js +1 -1
  98. package/lib/src/react/all-types-export.js.map +1 -1
  99. package/lib/src/react/index.d.ts +103 -5
  100. package/lib/src/react/index.d.ts.map +1 -1
  101. package/lib/src/react/index.js +80 -0
  102. package/lib/src/react/index.js.map +1 -1
  103. package/lib/src/react/index.spec.js +23 -1
  104. package/lib/src/react/index.spec.js.map +1 -1
  105. package/lib/src/types.d.ts +18 -0
  106. package/lib/src/types.d.ts.map +1 -1
  107. package/lib/src/types.js.map +1 -1
  108. package/lib/src/utils.d.ts +27 -0
  109. package/lib/src/utils.d.ts.map +1 -1
  110. package/lib/src/utils.js +31 -0
  111. package/lib/src/utils.js.map +1 -1
  112. package/lib/src/utils.spec.d.ts +0 -3
  113. package/lib/src/utils.spec.d.ts.map +1 -1
  114. package/lib/src/utils.spec.js +60 -7
  115. package/lib/src/utils.spec.js.map +1 -1
  116. package/lib/src/visual-embed-sdk.d.ts +61 -27
  117. package/package.json +1 -1
  118. package/src/embed/app.ts +6 -11
  119. package/src/embed/liveboard.ts +2 -11
  120. package/src/embed/sage.ts +2 -11
  121. package/src/embed/search.ts +2 -12
  122. package/src/embed/ts-embed.spec.ts +127 -0
  123. package/src/embed/ts-embed.ts +139 -120
  124. package/src/react/all-types-export.ts +5 -1
  125. package/src/react/index.spec.tsx +42 -1
  126. package/src/react/index.tsx +120 -17
  127. package/src/types.ts +19 -0
  128. package/src/utils.spec.ts +78 -7
  129. package/src/utils.ts +29 -1
package/src/embed/sage.ts CHANGED
@@ -168,22 +168,13 @@ export class SageEmbed extends V1Embed {
168
168
  /**
169
169
  * Render the embedded ThoughtSpot Sage
170
170
  *
171
- * @param showPreRenderByDefault
172
171
  * @returns {SageEmbed} Eureka/Sage embed
173
172
  */
174
- public render(showPreRenderByDefault = false): SageEmbed {
173
+ public render(): SageEmbed {
175
174
  super.render();
176
175
 
177
176
  const src = this.getIFrameSrc();
178
- this.renderV1Embed(src, showPreRenderByDefault);
179
-
180
- return this;
181
- }
182
-
183
- public preRender(showPreRenderByDefault = false): SageEmbed {
184
- super.preRender(showPreRenderByDefault);
185
-
186
- this.render(showPreRenderByDefault);
177
+ this.renderV1Embed(src);
187
178
 
188
179
  return this;
189
180
  }
@@ -259,15 +259,13 @@ export class SearchEmbed extends TsEmbed {
259
259
 
260
260
  /**
261
261
  * Render the embedded ThoughtSpot search
262
- *
263
- * @param showPreRenderByDefault
264
262
  */
265
- public render(showPreRenderByDefault = false): SearchEmbed {
263
+ public render(): SearchEmbed {
266
264
  super.render();
267
265
  const { answerId } = this.viewConfig;
268
266
 
269
267
  const src = this.getIFrameSrc(answerId);
270
- this.renderIFrame(src, showPreRenderByDefault);
268
+ this.renderIFrame(src);
271
269
  getAuthPromise().then(() => {
272
270
  if (
273
271
  checkReleaseVersionInBeta(
@@ -280,12 +278,4 @@ export class SearchEmbed extends TsEmbed {
280
278
  });
281
279
  return this;
282
280
  }
283
-
284
- public preRender(showPreRenderByDefault = false): SearchEmbed {
285
- super.preRender();
286
-
287
- this.render(showPreRenderByDefault);
288
-
289
- return this;
290
- }
291
281
  }
@@ -1258,4 +1258,131 @@ describe('Unit test case for ts embed', () => {
1258
1258
  });
1259
1259
  });
1260
1260
  });
1261
+
1262
+ describe('validate preRender flow', () => {
1263
+ beforeAll(() => {
1264
+ init({
1265
+ thoughtSpotHost: 'tshost',
1266
+ authType: AuthType.None,
1267
+ });
1268
+ });
1269
+
1270
+ afterAll(() => {
1271
+ const rootEle = document.getElementById('myRoot');
1272
+ rootEle.remove();
1273
+ });
1274
+
1275
+ const createRootEleForEmbed = () => {
1276
+ const rootEle = document.createElement('div');
1277
+ rootEle.id = 'myRoot';
1278
+ const tsEmbedDiv = document.createElement('div');
1279
+ tsEmbedDiv.id = 'tsEmbedDiv';
1280
+ rootEle.appendChild(tsEmbedDiv);
1281
+ document.body.appendChild(rootEle);
1282
+ };
1283
+
1284
+ it('should preRender and hide the iframe', async () => {
1285
+ createRootEleForEmbed();
1286
+
1287
+ const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
1288
+ preRenderId: 'i-am-preRendered',
1289
+ liveboardId: 'myLiveboardId',
1290
+ });
1291
+
1292
+ libEmbed.preRender();
1293
+
1294
+ await waitFor(() => !!getIFrameEl());
1295
+
1296
+ const preRenderIds = libEmbed.getPreRenderIds();
1297
+ const preRenderWrapper = document.getElementById(preRenderIds.wrapper);
1298
+ expect(preRenderWrapper.style.opacity).toBe('0');
1299
+ expect(preRenderWrapper.style.pointerEvents).toBe('none');
1300
+ expect(preRenderWrapper.style.zIndex).toBe('-1000');
1301
+
1302
+ const preRenderChild = (document
1303
+ .getElementById(preRenderIds.child) as HTMLIFrameElement);
1304
+ expect(preRenderWrapper.children[0]).toEqual(preRenderChild);
1305
+ expect(preRenderChild).toBeInstanceOf(HTMLIFrameElement);
1306
+ expect(preRenderChild.src).toMatch(/^http:\/\/tshost.*\/myLiveboardId/);
1307
+
1308
+ const tsEmbedDiv = document.getElementById('tsEmbedDiv');
1309
+ tsEmbedDiv.style.width = '100px';
1310
+ tsEmbedDiv.style.height = '100px';
1311
+
1312
+ let resizeObserverCb: any;
1313
+ (window as any).ResizeObserver = window.ResizeObserver
1314
+ || jest.fn().mockImplementation((resizeObserverCbParam) => {
1315
+ resizeObserverCb = resizeObserverCbParam;
1316
+ return ({
1317
+ disconnect: jest.fn(),
1318
+ observe: jest.fn(),
1319
+ unobserve: jest.fn(),
1320
+ });
1321
+ });
1322
+
1323
+ // show preRender
1324
+ libEmbed.showPreRender();
1325
+
1326
+ resizeObserverCb([{
1327
+ target: tsEmbedDiv,
1328
+ contentRect: { height: 297, width: 987 },
1329
+ }]);
1330
+
1331
+ expect(preRenderWrapper.style.height).toEqual(`${297}px`);
1332
+ expect(preRenderWrapper.style.width).toEqual(`${987}px`);
1333
+
1334
+ expect(preRenderWrapper.style.opacity).toBe('');
1335
+ expect(preRenderWrapper.style.pointerEvents).toBe('');
1336
+ expect(preRenderWrapper.style.zIndex).toBe('');
1337
+
1338
+ libEmbed.hidePreRender();
1339
+ expect(preRenderWrapper.style.opacity).toBe('0');
1340
+ expect(preRenderWrapper.style.pointerEvents).toBe('none');
1341
+ expect(preRenderWrapper.style.zIndex).toBe('-1000');
1342
+
1343
+ libEmbed.destroy();
1344
+ expect(document.getElementById(preRenderIds.wrapper)).toBe(null);
1345
+ });
1346
+
1347
+ it('preRender called without preRenderId should log error ', () => {
1348
+ createRootEleForEmbed();
1349
+
1350
+ spyOn(console, 'error');
1351
+ const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
1352
+ liveboardId: 'myLiveboardId',
1353
+ });
1354
+ libEmbed.preRender();
1355
+
1356
+ expect(console.error).toHaveBeenCalledWith('PreRender id is required for preRender');
1357
+ });
1358
+
1359
+ it('showPreRender should preRender if not available', async () => {
1360
+ createRootEleForEmbed();
1361
+
1362
+ const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
1363
+ preRenderId: 'i-am-preRendered',
1364
+ liveboardId: 'myLiveboardId',
1365
+ });
1366
+ const preRenderIds = libEmbed.getPreRenderIds();
1367
+ libEmbed.showPreRender();
1368
+ await waitFor(() => !!getIFrameEl());
1369
+ const preRenderWrapper = document.getElementById(preRenderIds.wrapper);
1370
+
1371
+ expect(preRenderWrapper.style.opacity).toBe('');
1372
+ expect(preRenderWrapper.style.pointerEvents).toBe('');
1373
+ expect(preRenderWrapper.style.zIndex).toBe('');
1374
+ });
1375
+
1376
+ it('hidePreRender should not preRender if not available', async () => {
1377
+ createRootEleForEmbed();
1378
+
1379
+ const libEmbed = new LiveboardEmbed('#tsEmbedDiv', {
1380
+ preRenderId: 'i-am-preRendered',
1381
+ liveboardId: 'myLiveboardId',
1382
+ });
1383
+ spyOn(libEmbed, 'preRender');
1384
+ libEmbed.hidePreRender();
1385
+ expect(libEmbed.preRender).toHaveBeenCalledTimes(0);
1386
+ });
1387
+ });
1261
1388
  });
@@ -136,6 +136,11 @@ export class TsEmbed {
136
136
  */
137
137
  private isError: boolean;
138
138
 
139
+ /**
140
+ * A flag that is set to true post preRender.
141
+ */
142
+ private isPreRendered: boolean;
143
+
139
144
  /**
140
145
  * Should we encode URL Query Params using base64 encoding which thoughtspot
141
146
  * will generate for embedding. This provides additional security to
@@ -147,6 +152,8 @@ export class TsEmbed {
147
152
 
148
153
  private defaultHiddenActions = [Action.ReportError];
149
154
 
155
+ private resizeObserver: ResizeObserver;
156
+
150
157
  constructor(domSelector: DOMSelector, viewConfig?: ViewConfig) {
151
158
  this.el = getDOMNode(domSelector);
152
159
  // TODO: handle error
@@ -377,7 +384,8 @@ export class TsEmbed {
377
384
  queryParams[Param.ViewPortWidth] = window.innerWidth;
378
385
  queryParams[Param.Version] = version;
379
386
  queryParams[Param.AuthType] = this.embedConfig.authType;
380
- queryParams[Param.blockNonEmbedFullAppAccess] = this.embedConfig.blockNonEmbedFullAppAccess ?? true;
387
+ queryParams[Param.blockNonEmbedFullAppAccess] = this.embedConfig.blockNonEmbedFullAppAccess
388
+ ?? true;
381
389
  if (this.embedConfig.disableLoginRedirect === true || this.embedConfig.autoLogin === true) {
382
390
  queryParams[Param.DisableLoginRedirect] = true;
383
391
  }
@@ -454,7 +462,8 @@ export class TsEmbed {
454
462
  queryParams[Param.ContextMenuTrigger] = false;
455
463
  }
456
464
 
457
- const spriteUrl = customizations?.iconSpriteUrl || this.embedConfig.customizations?.iconSpriteUrl;
465
+ const spriteUrl = customizations?.iconSpriteUrl
466
+ || this.embedConfig.customizations?.iconSpriteUrl;
458
467
  if (spriteUrl) {
459
468
  queryParams[Param.IconSpriteUrl] = spriteUrl.replace('https://', '');
460
469
  }
@@ -533,7 +542,9 @@ export class TsEmbed {
533
542
  // @ts-ignore
534
543
  iFrame.allow = 'clipboard-read; clipboard-write';
535
544
 
536
- const { height: frameHeight, width: frameWidth, ...restParams } = this.viewConfig.frameParams || {};
545
+ const {
546
+ height: frameHeight, width: frameWidth, ...restParams
547
+ } = this.viewConfig.frameParams || {};
537
548
  const width = getCssDimension(frameWidth || DEFAULT_EMBED_WIDTH);
538
549
  const height = getCssDimension(frameHeight || DEFAULT_EMBED_HEIGHT);
539
550
  setAttributes(iFrame, restParams);
@@ -545,12 +556,9 @@ export class TsEmbed {
545
556
  return iFrame;
546
557
  }
547
558
 
548
- protected handleInsertionIntoDOM(child: string | Node, showPreRenderByDefault = false): void {
559
+ protected handleInsertionIntoDOM(child: string | Node): void {
549
560
  if (this.isPreRendered) {
550
- if (!this.viewConfig.preRenderId) {
551
- throw Error('PreRender id is required for preRender');
552
- }
553
- this.insertIntoDOMForPreRender(child, showPreRenderByDefault);
561
+ this.insertIntoDOMForPreRender(child);
554
562
  } else {
555
563
  this.insertIntoDOM(child);
556
564
  }
@@ -561,9 +569,8 @@ export class TsEmbed {
561
569
  * event listeners.
562
570
  *
563
571
  * @param url - The URL of the embedded ThoughtSpot app.
564
- * @param showPreRenderByDefault - The flag to show the preRender by default.
565
572
  */
566
- protected async renderIFrame(url: string, showPreRenderByDefault = false): Promise<any> {
573
+ protected async renderIFrame(url: string): Promise<any> {
567
574
  if (this.isError) {
568
575
  return null;
569
576
  }
@@ -588,10 +595,7 @@ export class TsEmbed {
588
595
  return getAuthPromise()
589
596
  ?.then((isLoggedIn: boolean) => {
590
597
  if (!isLoggedIn) {
591
- this.handleInsertionIntoDOM(
592
- this.embedConfig.loginFailedMessage,
593
- showPreRenderByDefault,
594
- );
598
+ this.handleInsertionIntoDOM(this.embedConfig.loginFailedMessage);
595
599
  return;
596
600
  }
597
601
 
@@ -614,7 +618,7 @@ export class TsEmbed {
614
618
  this.iFrame.addEventListener('error', () => {
615
619
  nextInQueue();
616
620
  });
617
- this.handleInsertionIntoDOM(this.iFrame, showPreRenderByDefault);
621
+ this.handleInsertionIntoDOM(this.iFrame);
618
622
  const prefetchIframe = document.querySelectorAll('.prefetchIframe');
619
623
  if (prefetchIframe.length) {
620
624
  prefetchIframe.forEach((el) => {
@@ -634,15 +638,6 @@ export class TsEmbed {
634
638
  });
635
639
  }
636
640
 
637
- public getPreRenderIds() {
638
- return {
639
- wrapper: `tsEmbed-pre-render-wrapper-${this.viewConfig.preRenderId}`,
640
- // shield:
641
- // `tsEmbed-pre-render-shield-${this.viewConfig.preRenderId}`,
642
- child: `tsEmbed-pre-render-child-${this.viewConfig.preRenderId}`,
643
- };
644
- }
645
-
646
641
  protected createPreRenderWrapper(): HTMLDivElement {
647
642
  if (!this.viewConfig.preRenderId) {
648
643
  throw new Error('PreRender id is required to create PreRender wrapper');
@@ -661,32 +656,18 @@ export class TsEmbed {
661
656
  };
662
657
  setStyleProperties(preRenderWrapper, initialPreRenderWrapperStyle);
663
658
 
664
- // const preRenderShield = document.createElement('div');
665
- // preRenderShield.id = preRenderIds.shield;
666
- // setStyleProperties(preRenderShield, { position: 'absolute',
667
- // width: '100%', height: '100%' });
668
-
669
- // preRenderWrapper.appendChild(preRenderShield);
670
-
671
- // this.preRenderWrapper = preRenderWrapper;
672
- // this.preRenderShield = preRenderShield;
673
- // this.preRenderChild = child;
674
-
675
659
  return preRenderWrapper;
676
660
  }
677
661
 
678
662
  protected preRenderWrapper: HTMLElement;
679
663
 
680
- // protected preRenderShield: HTMLElement;
681
-
682
664
  protected preRenderChild: HTMLElement;
683
665
 
684
666
  protected connectPreRendered(): boolean {
685
667
  const preRenderIds = this.getPreRenderIds();
686
668
  this.preRenderWrapper = this.preRenderWrapper
687
- || document.getElementById(preRenderIds.wrapper);
688
- // this.preRenderShield = this.preRenderShield
689
- // || document.getElementById(preRenderIds.shield);
669
+ || document.getElementById(preRenderIds.wrapper);
670
+
690
671
  this.preRenderChild = this.preRenderChild || document.getElementById(preRenderIds.child);
691
672
 
692
673
  if (this.preRenderWrapper && this.preRenderChild) {
@@ -698,7 +679,7 @@ export class TsEmbed {
698
679
  }
699
680
 
700
681
  protected isPreRenderAvailable(): boolean {
701
- return this.isPreRendered;
682
+ return this.isPreRendered && Boolean(this.preRenderWrapper && this.preRenderChild);
702
683
  }
703
684
 
704
685
  protected createPreRenderChild(child: string | Node): HTMLElement {
@@ -724,10 +705,7 @@ export class TsEmbed {
724
705
  return divChildNode;
725
706
  }
726
707
 
727
- protected insertIntoDOMForPreRender(
728
- child: string | Node,
729
- showPreRenderByDefault = false,
730
- ): void {
708
+ protected insertIntoDOMForPreRender(child: string | Node): void {
731
709
  const preRenderChild = this.createPreRenderChild(child);
732
710
  const preRenderWrapper = this.createPreRenderWrapper();
733
711
  preRenderWrapper.appendChild(preRenderChild);
@@ -739,83 +717,17 @@ export class TsEmbed {
739
717
  this.iFrame = preRenderChild;
740
718
  }
741
719
 
742
- if (showPreRenderByDefault) {
720
+ if (this.showPreRenderByDefault) {
743
721
  this.showPreRender();
744
722
  } else {
745
723
  this.hidePreRender();
746
724
  }
747
725
 
726
+ this.insertedDomEl = preRenderWrapper;
748
727
  document.body.appendChild(preRenderWrapper);
749
728
  }
750
729
 
751
- public hidePreRender(): void {
752
- if (!this.isPreRenderAvailable()) {
753
- // if the embed component is not preRendered , nothing to hide
754
- console.warn(
755
- 'Warning: You should call PreRender before hiding it using hidePreRender.',
756
- );
757
- return;
758
- }
759
- const preRenderHideStyles = {
760
- opacity: '0',
761
- pointerEvents: 'none',
762
- zIndex: '-1000',
763
- position: 'absolute ',
764
- top: '0',
765
- left: '0',
766
- };
767
- setStyleProperties(this.preRenderWrapper, preRenderHideStyles);
768
-
769
- // const childBoundingRect = this.preRenderChild.getBoundingClientRect();
770
- //
771
- // setStyleProperties(this.preRenderShield, {
772
- // opacity: '0',
773
- // pointerEvents: 'none',
774
- // zIndex: '1',
775
- // width: `${childBoundingRect.width}px`,
776
- // height: `${childBoundingRect.height}px`,
777
- // position: 'absolute',
778
- // top: '0',
779
- // left: '0',
780
- // });
781
-
782
- this.unsubscribeToEvents();
783
- }
784
-
785
- public showPreRender(): void {
786
- if (!this.isPreRenderAvailable()) {
787
- const isAvailable = this.connectPreRendered();
788
- if (!isAvailable) {
789
- // if the Embed component is not preRendered , Render it now and
790
- this.preRender(true);
791
- // show it (hide is defalt behaviour)
792
- // console.log('No preRender found, creating new ');
793
- return;
794
- }
795
- }
796
-
797
- this.syncPreRenderStyle();
798
-
799
- removeStyleProperties(this.preRenderWrapper, ['z-index', 'opacity', 'pointer-events']);
800
-
801
- // setStyleProperties(this.preRenderShield, { zIndex: '-1' });
802
-
803
- this.subscribeToEvents();
804
- }
805
-
806
- public syncPreRenderStyle(): void {
807
- if (!this.el) {
808
- throw new Error('Embed element is not defined');
809
- }
810
- const elBoundingClient = this.el.getBoundingClientRect();
811
-
812
- setStyleProperties(this.preRenderWrapper, {
813
- top: `${elBoundingClient.y}px`,
814
- left: `${elBoundingClient.x}px`,
815
- width: `${elBoundingClient.width}px`,
816
- height: `${elBoundingClient.height}px`,
817
- });
818
- }
730
+ private showPreRenderByDefault = false;
819
731
 
820
732
  protected insertIntoDOM(child: string | Node): void {
821
733
  if (this.viewConfig.insertAsSibling) {
@@ -1048,15 +960,19 @@ export class TsEmbed {
1048
960
  return this;
1049
961
  }
1050
962
 
1051
- private isPreRendered: boolean;
1052
-
1053
963
  /**
1054
964
  * Creates the preRender shell
1055
965
  *
1056
- * @param showPreRenderByDefault
966
+ * @param showPreRenderByDefault - Show the preRender after render, hidden by default
1057
967
  */
1058
968
  public preRender(showPreRenderByDefault = false): TsEmbed {
969
+ if (!this.viewConfig.preRenderId) {
970
+ console.error('PreRender id is required for preRender');
971
+ return this;
972
+ }
1059
973
  this.isPreRendered = true;
974
+ this.showPreRenderByDefault = showPreRenderByDefault;
975
+ this.render();
1060
976
  return this;
1061
977
  }
1062
978
 
@@ -1120,6 +1036,110 @@ export class TsEmbed {
1120
1036
  const prerenderFrameSrc = this.getRootIframeSrc();
1121
1037
  return this.renderIFrame(prerenderFrameSrc);
1122
1038
  }
1039
+
1040
+ /**
1041
+ * Displays the PreRender component.
1042
+ * If the component is not preRendered, it attempts to create and render it.
1043
+ * Also, synchronizes the style of the PreRender component with the embedding
1044
+ * element.
1045
+ */
1046
+ public showPreRender(): void {
1047
+ if (!this.isPreRenderAvailable()) {
1048
+ const isAvailable = this.connectPreRendered();
1049
+ if (!isAvailable) {
1050
+ // if the Embed component is not preRendered , Render it now and
1051
+ this.preRender(true);
1052
+ return;
1053
+ }
1054
+ }
1055
+
1056
+ if (this.el) {
1057
+ this.syncPreRenderStyle();
1058
+
1059
+ this.resizeObserver = new ResizeObserver((entries) => {
1060
+ entries.forEach((entry) => {
1061
+ if (entry.contentRect && entry.target === this.el) {
1062
+ setStyleProperties(this.preRenderWrapper, {
1063
+ width: `${entry.contentRect.width}px`,
1064
+ height: `${entry.contentRect.height}px`,
1065
+ });
1066
+ }
1067
+ });
1068
+ });
1069
+ this.resizeObserver.observe(this.el);
1070
+ }
1071
+
1072
+ removeStyleProperties(this.preRenderWrapper, ['z-index', 'opacity', 'pointer-events']);
1073
+
1074
+ this.subscribeToEvents();
1075
+ }
1076
+
1077
+ /**
1078
+ * Synchronizes the style properties of the PreRender component with the embedding
1079
+ * element. This function adjusts the position, width, and height of the PreRender
1080
+ * component
1081
+ * to match the dimensions and position of the embedding element.
1082
+ *
1083
+ * @throws {Error} Throws an error if the embedding element (passed as domSelector)
1084
+ * is not defined or not found.
1085
+ */
1086
+ public syncPreRenderStyle(): void {
1087
+ if (!this.el) {
1088
+ throw new Error('Embed element is not defined');
1089
+ }
1090
+ const elBoundingClient = this.el.getBoundingClientRect();
1091
+
1092
+ setStyleProperties(this.preRenderWrapper, {
1093
+ top: `${elBoundingClient.y}px`,
1094
+ left: `${elBoundingClient.x}px`,
1095
+ width: `${elBoundingClient.width}px`,
1096
+ height: `${elBoundingClient.height}px`,
1097
+ });
1098
+ }
1099
+
1100
+ /**
1101
+ * Hides the PreRender component if it is available.
1102
+ * If the component is not preRendered, it issues a warning.
1103
+ */
1104
+ public hidePreRender(): void {
1105
+ if (!this.isPreRenderAvailable()) {
1106
+ // if the embed component is not preRendered , nothing to hide
1107
+ console.warn(
1108
+ 'Warning: You should call PreRender before hiding it using hidePreRender.',
1109
+ );
1110
+ return;
1111
+ }
1112
+ const preRenderHideStyles = {
1113
+ opacity: '0',
1114
+ pointerEvents: 'none',
1115
+ zIndex: '-1000',
1116
+ position: 'absolute ',
1117
+ top: '0',
1118
+ left: '0',
1119
+ };
1120
+ setStyleProperties(this.preRenderWrapper, preRenderHideStyles);
1121
+
1122
+ if (this.resizeObserver) {
1123
+ this.resizeObserver.disconnect();
1124
+ }
1125
+
1126
+ this.unsubscribeToEvents();
1127
+ }
1128
+
1129
+ /**
1130
+ * Retrieves unique HTML element IDs for PreRender-related elements.
1131
+ * These IDs are constructed based on the provided 'preRenderId' from 'viewConfig'.
1132
+ *
1133
+ * @returns {object} An object containing the IDs for the PreRender elements.
1134
+ * @property {string} wrapper - The HTML element ID for the PreRender wrapper.
1135
+ * @property {string} child - The HTML element ID for the PreRender child.
1136
+ */
1137
+ public getPreRenderIds() {
1138
+ return {
1139
+ wrapper: `tsEmbed-pre-render-wrapper-${this.viewConfig.preRenderId}`,
1140
+ child: `tsEmbed-pre-render-child-${this.viewConfig.preRenderId}`,
1141
+ };
1142
+ }
1123
1143
  }
1124
1144
 
1125
1145
  /**
@@ -1141,10 +1161,9 @@ export class V1Embed extends TsEmbed {
1141
1161
  * Render the app in an iframe and set up event handlers
1142
1162
  *
1143
1163
  * @param iframeSrc
1144
- * @param showPreRenderByDefault - if true the preRender will be shown by default
1145
1164
  */
1146
- protected renderV1Embed(iframeSrc: string, showPreRenderByDefault = false): any {
1147
- return this.renderIFrame(iframeSrc, showPreRenderByDefault);
1165
+ protected renderV1Embed(iframeSrc: string): Promise<any> {
1166
+ return this.renderIFrame(iframeSrc);
1148
1167
  }
1149
1168
 
1150
1169
  protected getRootIframeSrc(): string {
@@ -1,11 +1,15 @@
1
1
  export {
2
2
  SearchEmbed,
3
+ PreRenderedSearchEmbed,
3
4
  LiveboardEmbed,
5
+ PreRenderedLiveboardEmbed,
4
6
  SearchBarEmbed,
7
+ PreRenderedSearchBarEmbed,
5
8
  AppEmbed,
9
+ PreRenderedAppEmbed,
6
10
  SageEmbed,
11
+ PreRenderedSageEmbed,
7
12
  useEmbedRef,
8
- PreRenderedLiveboardEmbed,
9
13
  } from './index';
10
14
 
11
15
  export {
@@ -15,7 +15,7 @@ import {
15
15
  mockMessageChannel,
16
16
  } from '../test/test-utils';
17
17
  import {
18
- SearchEmbed, AppEmbed, LiveboardEmbed, useEmbedRef, SearchBarEmbed,
18
+ SearchEmbed, AppEmbed, LiveboardEmbed, useEmbedRef, SearchBarEmbed, PreRenderedLiveboardEmbed,
19
19
  } from './index';
20
20
  import * as allExports from './index';
21
21
  import {
@@ -226,6 +226,47 @@ describe('React Components', () => {
226
226
  );
227
227
  });
228
228
  });
229
+
230
+ describe('PreRenderedLiveboardEmbed', () => {
231
+ it('should preRender the liveboard ', async () => {
232
+ const preRenderId = 'tsEmbed-pre-render-wrapper-test';
233
+
234
+ const { container } = render(
235
+ <PreRenderedLiveboardEmbed
236
+ className="embedClass"
237
+ preRenderId="test"
238
+ liveboardId="libId"
239
+ />,
240
+ );
241
+
242
+ await waitFor(() => getIFrameEl());
243
+ const preRenderWrapper = document.body.querySelector(`#${preRenderId}`) as HTMLDivElement;
244
+
245
+ expect(preRenderWrapper).toBeInstanceOf(HTMLDivElement);
246
+ expect((preRenderWrapper as HTMLDivElement).childElementCount).toBe(1);
247
+
248
+ const preRenderChildId = 'tsEmbed-pre-render-child-test';
249
+ const preRenderChild = document.body.querySelector(`#${preRenderChildId}`);
250
+ expect(preRenderWrapper.children[0]).toBe(preRenderChild);
251
+
252
+ (window as any).ResizeObserver = jest.fn().mockImplementation(() => ({
253
+ disconnect: jest.fn(),
254
+ observe: jest.fn(),
255
+ unobserve: jest.fn(),
256
+ }));
257
+ const { container: libContainer } = render(
258
+ <LiveboardEmbed
259
+ className="embedClass"
260
+ preRenderId="test"
261
+ liveboardId="libId"
262
+ />,
263
+ );
264
+
265
+ expect(preRenderWrapper.style.opacity).toBe('');
266
+ expect(preRenderWrapper.style.pointerEvents).toBe('');
267
+ expect(preRenderWrapper.style.zIndex).toBe('');
268
+ });
269
+ });
229
270
  });
230
271
 
231
272
  describe('allExports', () => {