@thoughtspot/visual-embed-sdk 1.9.3 → 1.10.0-alpha.2

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 (54) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/src/embed/app.d.ts +8 -0
  3. package/dist/src/embed/liveboard.d.ts +8 -0
  4. package/dist/src/embed/search.d.ts +0 -4
  5. package/dist/src/embed/ts-embed.d.ts +16 -1
  6. package/dist/src/types.d.ts +61 -7
  7. package/dist/src/utils.d.ts +3 -0
  8. package/dist/tsembed.es.js +111 -15
  9. package/dist/tsembed.js +111 -15
  10. package/lib/package.json +1 -1
  11. package/lib/src/embed/app.d.ts +8 -0
  12. package/lib/src/embed/app.js +5 -2
  13. package/lib/src/embed/app.js.map +1 -1
  14. package/lib/src/embed/app.spec.js +11 -10
  15. package/lib/src/embed/app.spec.js.map +1 -1
  16. package/lib/src/embed/liveboard.d.ts +8 -0
  17. package/lib/src/embed/liveboard.js +4 -1
  18. package/lib/src/embed/liveboard.js.map +1 -1
  19. package/lib/src/embed/liveboard.spec.js +1 -1
  20. package/lib/src/embed/liveboard.spec.js.map +1 -1
  21. package/lib/src/embed/pinboard.spec.js +1 -1
  22. package/lib/src/embed/pinboard.spec.js.map +1 -1
  23. package/lib/src/embed/search.d.ts +0 -4
  24. package/lib/src/embed/search.js +3 -2
  25. package/lib/src/embed/search.js.map +1 -1
  26. package/lib/src/embed/ts-embed.d.ts +16 -1
  27. package/lib/src/embed/ts-embed.js +34 -4
  28. package/lib/src/embed/ts-embed.js.map +1 -1
  29. package/lib/src/embed/ts-embed.spec.js +52 -7
  30. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  31. package/lib/src/react/index.js +1 -1
  32. package/lib/src/react/index.js.map +1 -1
  33. package/lib/src/react/index.spec.js +2 -1
  34. package/lib/src/react/index.spec.js.map +1 -1
  35. package/lib/src/types.d.ts +61 -7
  36. package/lib/src/types.js +60 -6
  37. package/lib/src/types.js.map +1 -1
  38. package/lib/src/utils.d.ts +3 -0
  39. package/lib/src/utils.js +5 -0
  40. package/lib/src/utils.js.map +1 -1
  41. package/lib/src/visual-embed-sdk.d.ts +93 -12
  42. package/package.json +1 -1
  43. package/src/embed/app.spec.ts +11 -10
  44. package/src/embed/app.ts +14 -2
  45. package/src/embed/liveboard.spec.ts +1 -1
  46. package/src/embed/liveboard.ts +13 -0
  47. package/src/embed/pinboard.spec.ts +1 -1
  48. package/src/embed/search.ts +4 -6
  49. package/src/embed/ts-embed.spec.ts +75 -7
  50. package/src/embed/ts-embed.ts +49 -3
  51. package/src/react/index.spec.tsx +6 -1
  52. package/src/react/index.tsx +7 -1
  53. package/src/types.ts +61 -7
  54. package/src/utils.ts +9 -0
@@ -49,10 +49,6 @@ export interface SearchViewConfig extends ViewConfig {
49
49
  * using raw answer data.
50
50
  */
51
51
  hideResults?: boolean;
52
- /**
53
- * If set to true, expands all the data sources panel.
54
- */
55
- expandAllDataSource?: boolean;
56
52
  /**
57
53
  * If set to true, the Search Assist feature is enabled.
58
54
  */
@@ -130,7 +126,6 @@ export class SearchEmbed extends TsEmbed {
130
126
  private getIFrameSrc(answerId: string, dataSources?: string[]) {
131
127
  const {
132
128
  hideResults,
133
- expandAllDataSource,
134
129
  enableSearchAssist,
135
130
  forceTable,
136
131
  searchOptions,
@@ -173,8 +168,11 @@ export class SearchEmbed extends TsEmbed {
173
168
  if (queryParamsString) {
174
169
  query = `?${queryParamsString}`;
175
170
  }
171
+ const tsPostHashParams = this.getThoughtSpotPostUrlParams();
176
172
 
177
- return `${this.getEmbedBasePath(query)}/${answerPath}`;
173
+ return `${this.getEmbedBasePath(
174
+ query,
175
+ )}/${answerPath}${tsPostHashParams}`;
178
176
  }
179
177
 
180
178
  /**
@@ -10,7 +10,12 @@ import {
10
10
  LiveboardEmbed,
11
11
  } from '../index';
12
12
  import { Action } from '../types';
13
- import { getDocumentBody, getIFrameSrc, getRootEl } from '../test/test-utils';
13
+ import {
14
+ getDocumentBody,
15
+ getIFrameEl,
16
+ getIFrameSrc,
17
+ getRootEl,
18
+ } from '../test/test-utils';
14
19
  import * as config from '../config';
15
20
  import * as tsEmbedInstance from './ts-embed';
16
21
  import * as mixpanelInstance from '../mixpanel-service';
@@ -28,6 +33,7 @@ const pinboardId = 'eca215d4-0d2c-4a55-90e3-d81ef6848ae0';
28
33
  const liveboardId = 'eca215d4-0d2c-4a55-90e3-d81ef6848ae0';
29
34
  const thoughtSpotHost = 'tshost';
30
35
  const defaultParamsForPinboardEmbed = `hostAppUrl=local-host&viewPortHeight=768&viewPortWidth=1024&sdkVersion=${version}`;
36
+ const defaultParamsPost = '&isPinboardV2Enabled=false';
31
37
 
32
38
  describe('Unit test case for ts embed', () => {
33
39
  const mockMixPanelEvent = jest.spyOn(
@@ -227,7 +233,7 @@ describe('Unit test case for ts embed', () => {
227
233
  });
228
234
  });
229
235
 
230
- describe('Naviage to Page API', () => {
236
+ describe('Navigate to Page API', () => {
231
237
  const path = 'viz/e0836cad-4fdf-42d4-bd97-567a6b2a6058';
232
238
  beforeEach(() => {
233
239
  jest.spyOn(config, 'getThoughtSpotHost').mockImplementation(
@@ -242,7 +248,7 @@ describe('Unit test case for ts embed', () => {
242
248
  await pinboardEmbed.render();
243
249
  // pinboardEmbed.navigateToPage(path);
244
250
  expect(getIFrameSrc()).toBe(
245
- `http://${thoughtSpotHost}/?embedApp=true&${defaultParamsForPinboardEmbed}&isLiveboardEmbed=true#/embed/${path}`,
251
+ `http://${thoughtSpotHost}/?embedApp=true&${defaultParamsForPinboardEmbed}&isLiveboardEmbed=true${defaultParamsPost}#/embed/${path}`,
246
252
  );
247
253
  });
248
254
 
@@ -256,10 +262,23 @@ describe('Unit test case for ts embed', () => {
256
262
  await appEmbed.render();
257
263
  appEmbed.navigateToPage(path);
258
264
  expect(getIFrameSrc()).toBe(
259
- `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}#/${path}`,
265
+ `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}${defaultParamsPost}#/${path}`,
260
266
  );
261
267
  });
262
268
 
269
+ test('Set Frame params to the iframe as attributes', async () => {
270
+ const appEmbed = new AppEmbed(getRootEl(), {
271
+ frameParams: {
272
+ width: '100%',
273
+ height: '100%',
274
+ allowtransparency: true,
275
+ },
276
+ });
277
+ await appEmbed.render();
278
+ const iframe = getIFrameEl();
279
+ expect(iframe.getAttribute('allowtransparency')).toBe('true');
280
+ });
281
+
263
282
  test('navigateToPage function use before render', async () => {
264
283
  spyOn(console, 'log');
265
284
  const appEmbed = new AppEmbed(getRootEl(), {
@@ -275,7 +294,7 @@ describe('Unit test case for ts embed', () => {
275
294
  );
276
295
  });
277
296
  });
278
- describe('Naviage to Page API - Pinboard', () => {
297
+ describe('Navigate to Page API - Pinboard', () => {
279
298
  const path = 'pinboard/e0836cad-4fdf-42d4-bd97-567a6b2a6058';
280
299
  beforeEach(() => {
281
300
  jest.spyOn(config, 'getThoughtSpotHost').mockImplementation(
@@ -293,7 +312,7 @@ describe('Unit test case for ts embed', () => {
293
312
  await appEmbed.render();
294
313
  appEmbed.navigateToPage(path);
295
314
  expect(getIFrameSrc()).toBe(
296
- `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}#/${path}`,
315
+ `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}${defaultParamsPost}#/${path}`,
297
316
  );
298
317
  });
299
318
  });
@@ -319,8 +338,57 @@ describe('Unit test case for ts embed', () => {
319
338
  await appEmbed.render();
320
339
  expect(getIFrameSrc()).toBe(
321
340
  `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}` +
322
- '&foo=bar&baz=1&bool=true#/home',
341
+ `&foo=bar&baz=1&bool=true${defaultParamsPost}#/home`,
342
+ );
343
+ });
344
+ });
345
+
346
+ describe('validate getThoughtSpotPostUrlParams', () => {
347
+ const { location } = window;
348
+
349
+ beforeAll(() => {
350
+ delete window.location;
351
+ (window as any).location = {
352
+ hash: '',
353
+ search: '',
354
+ };
355
+ });
356
+
357
+ beforeEach(() => {
358
+ jest.spyOn(config, 'getThoughtSpotHost').mockImplementation(
359
+ () => 'http://tshost',
360
+ );
361
+ });
362
+
363
+ afterAll((): void => {
364
+ window.location = location;
365
+ });
366
+
367
+ it('get url params for TS', () => {
368
+ const tsEmbed = new tsEmbedInstance.TsEmbed(
369
+ getRootEl(),
370
+ defaultViewConfig,
371
+ );
372
+ const urlHash =
373
+ '#/analyze?ts-app=thoughtspot&ts-id=123&title=embed-sdk';
374
+ window.location.hash = urlHash;
375
+ const postHashParams = '?ts-app=thoughtspot&ts-id=123';
376
+ expect(tsEmbed.getThoughtSpotPostUrlParams()).toBe(postHashParams);
377
+ });
378
+
379
+ it('validate query params and postHash params for TS', () => {
380
+ const tsEmbed = new tsEmbedInstance.TsEmbed(
381
+ getRootEl(),
382
+ defaultViewConfig,
323
383
  );
384
+ const urlHash =
385
+ '#/analyze?ts-app=thoughtspot&ts-id=123&title=embed-sdk';
386
+ window.location.hash = urlHash;
387
+ const urlSearch = '?ts-type=subscribe&search-title=abc';
388
+ window.location.search = urlSearch;
389
+ const postHashParams =
390
+ '?ts-type=subscribe&ts-app=thoughtspot&ts-id=123';
391
+ expect(tsEmbed.getThoughtSpotPostUrlParams()).toBe(postHashParams);
324
392
  });
325
393
  });
326
394
  });
@@ -11,6 +11,7 @@ import {
11
11
  getEncodedQueryParamsString,
12
12
  getCssDimension,
13
13
  getOffsetTop,
14
+ setAttributes,
14
15
  } from '../utils';
15
16
  import {
16
17
  getThoughtSpotHost,
@@ -37,6 +38,11 @@ import { getAuthPromise, getEmbedConfig, renderInQueue } from './base';
37
38
 
38
39
  const { version } = pkgInfo;
39
40
 
41
+ /**
42
+ * Global prefix for all Thoughtspot postHash Params.
43
+ */
44
+ export const THOUGHTSPOT_PARAM_PREFIX = 'ts-';
45
+
40
46
  /**
41
47
  * The event id map from v2 event names to v1 event id
42
48
  * v1 events are the classic embed events implemented in Blink v1
@@ -62,6 +68,11 @@ export interface FrameParams {
62
68
  * The height of the iFrame (unit is pixels if numeric).
63
69
  */
64
70
  height?: number | string;
71
+ /**
72
+ * This parameters will be passed on the iframe
73
+ * as is.
74
+ */
75
+ [key: string]: string | number | boolean;
65
76
  }
66
77
 
67
78
  /**
@@ -403,7 +414,7 @@ export class TsEmbed {
403
414
  * @param url
404
415
  * @param frameOptions
405
416
  */
406
- protected renderIFrame(url: string, frameOptions: FrameParams): void {
417
+ protected renderIFrame(url: string, frameOptions: FrameParams = {}): void {
407
418
  if (this.isError) {
408
419
  return;
409
420
  }
@@ -445,12 +456,19 @@ export class TsEmbed {
445
456
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
446
457
  // @ts-ignore
447
458
  this.iFrame.mozallowfullscreen = true;
459
+ const {
460
+ height: frameHeight,
461
+ width: frameWidth,
462
+ ...restParams
463
+ } = frameOptions;
448
464
  const width = getCssDimension(
449
- frameOptions?.width || DEFAULT_EMBED_WIDTH,
465
+ frameWidth || DEFAULT_EMBED_WIDTH,
450
466
  );
451
467
  const height = getCssDimension(
452
- frameOptions?.height || DEFAULT_EMBED_HEIGHT,
468
+ frameWidth || DEFAULT_EMBED_HEIGHT,
453
469
  );
470
+ setAttributes(this.iFrame, restParams);
471
+
454
472
  this.iFrame.style.width = `${width}`;
455
473
  this.iFrame.style.height = `${height}`;
456
474
  this.iFrame.style.border = '0';
@@ -653,6 +671,34 @@ export class TsEmbed {
653
671
 
654
672
  return this;
655
673
  }
674
+
675
+ /**
676
+ * Get the Post Url Params for THOUGHTSPOT from the current
677
+ * host app URL.
678
+ * THOUGHTSPOT URL params starts with a prefix "ts-"
679
+ */
680
+ public getThoughtSpotPostUrlParams(): string {
681
+ const urlHash = window.location.hash;
682
+ const queryParams = window.location.search;
683
+ const postHashParams = urlHash.split('?');
684
+ const postURLParams = postHashParams[postHashParams.length - 1];
685
+ const queryParamsObj = new URLSearchParams(queryParams);
686
+ const postURLParamsObj = new URLSearchParams(postURLParams);
687
+ const params = new URLSearchParams();
688
+
689
+ const addKeyValuePairCb = (value: string, key: string): void => {
690
+ if (key.startsWith(THOUGHTSPOT_PARAM_PREFIX)) {
691
+ params.append(key, value);
692
+ }
693
+ };
694
+ queryParamsObj.forEach(addKeyValuePairCb);
695
+ postURLParamsObj.forEach(addKeyValuePairCb);
696
+
697
+ let tsParams = params.toString();
698
+ tsParams = tsParams ? `?${tsParams}` : '';
699
+
700
+ return tsParams;
701
+ }
656
702
  }
657
703
 
658
704
  /**
@@ -26,11 +26,16 @@ describe('React Components', () => {
26
26
  describe('SearchEmbed', () => {
27
27
  it('Should Render the Iframe with props', async () => {
28
28
  const { container } = render(
29
- <SearchEmbed hideDataSources={true} />,
29
+ <SearchEmbed hideDataSources={true} className="embedClass" />,
30
30
  );
31
31
 
32
32
  await waitFor(() => getIFrameEl(container));
33
33
 
34
+ expect(
35
+ getIFrameEl(container).parentElement.classList.contains(
36
+ 'embedClass',
37
+ ),
38
+ ).toBe(true);
34
39
  expect(getIFrameSrc(container)).toBe(
35
40
  `http://${thoughtSpotHost}/?hostAppUrl=local-host&viewPortHeight=768&viewPortWidth=1024&sdkVersion=${version}&hideAction=[%22editACopy%22,%22saveAsView%22,%22updateTSL%22,%22editTSL%22,%22onDeleteAnswer%22]&dataSourceMode=hide&useLastSelectedSources=false&isSearchEmbed=true#/embed/answer`,
36
41
  );
@@ -42,7 +42,13 @@ const componentFactory = <
42
42
  }
43
43
  }, [embedProps]);
44
44
 
45
- return <div data-testid="tsEmbed" ref={ref}></div>;
45
+ return (
46
+ <div
47
+ data-testid="tsEmbed"
48
+ ref={ref}
49
+ className={className}
50
+ ></div>
51
+ );
46
52
  },
47
53
  );
48
54
 
package/src/types.ts CHANGED
@@ -129,7 +129,7 @@ export interface EmbedConfig {
129
129
  * @version SDK: 1.9.3 | ThoughtSpot: 8.1.0.cl
130
130
  * @default false
131
131
  */
132
- disableLoginRedirect?: boolean;
132
+ disableLoginRedirect?: boolean;
133
133
 
134
134
  /**
135
135
  * Calls the prefetch method internally when set to true
@@ -459,7 +459,6 @@ export enum DataSourceVisualMode {
459
459
  export enum Param {
460
460
  DataSources = 'dataSources',
461
461
  DataSourceMode = 'dataSourceMode',
462
- ExpandAllDataSource = 'expandAllDataSource',
463
462
  DisableActions = 'disableAction',
464
463
  DisableActionReason = 'disableHint',
465
464
  ForceTable = 'forceTable',
@@ -483,8 +482,9 @@ export enum Param {
483
482
  ViewPortWidth = 'viewPortWidth',
484
483
  VisibleActions = 'visibleAction',
485
484
  CustomCSSUrl = 'customCssUrl',
486
- visibleVizs = 'pinboardVisibleVizs',
487
485
  DisableLoginRedirect = 'disableLoginRedirect',
486
+ visibleVizs = 'pinboardVisibleVizs',
487
+ LiveboardV2Enabled = 'isPinboardV2Enabled',
488
488
  }
489
489
 
490
490
  /**
@@ -494,30 +494,57 @@ export enum Param {
494
494
  // eslint-disable-next-line no-shadow
495
495
  export enum Action {
496
496
  Save = 'save',
497
+ /**
498
+ * @hidden
499
+ */
497
500
  Update = 'update',
501
+ /**
502
+ * @hidden
503
+ */
498
504
  SaveUntitled = 'saveUntitled',
499
505
  SaveAsView = 'saveAsView',
500
506
  MakeACopy = 'makeACopy',
501
507
  EditACopy = 'editACopy',
502
508
  CopyLink = 'embedDocument',
509
+ /**
510
+ * @hidden
511
+ */
503
512
  ResetLayout = 'resetLayout',
504
513
  Schedule = 'subscription',
505
514
  SchedulesList = 'schedule-list',
506
515
  Share = 'share',
507
516
  AddFilter = 'addFilter',
508
517
  ConfigureFilter = 'configureFilter',
518
+ /**
519
+ * @hidden
520
+ */
509
521
  AddFormula = 'addFormula',
522
+ /**
523
+ * @hidden
524
+ */
510
525
  SearchOnTop = 'searchOnTop',
511
526
  SpotIQAnalyze = 'spotIQAnalyze',
527
+ /**
528
+ * @hidden
529
+ */
512
530
  ExplainInsight = 'explainInsight',
531
+ /**
532
+ * @hidden
533
+ */
513
534
  SpotIQFollow = 'spotIQFollow',
514
535
  ShareViz = 'shareViz',
536
+ /**
537
+ * @hidden
538
+ */
515
539
  ReplaySearch = 'replaySearch',
516
540
  ShowUnderlyingData = 'showUnderlyingData',
517
541
  Download = 'download',
518
542
  DownloadAsPdf = 'downloadAsPdf',
519
543
  DownloadAsCsv = 'downloadAsCSV',
520
544
  DownloadAsXlsx = 'downloadAsXLSX',
545
+ /**
546
+ * @hidden
547
+ */
521
548
  DownloadTrace = 'downloadTrace',
522
549
  ExportTML = 'exportTSL',
523
550
  ImportTML = 'importTSL',
@@ -528,18 +555,39 @@ export enum Action {
528
555
  Edit = 'edit',
529
556
  EditTitle = 'editTitle',
530
557
  Remove = 'delete',
558
+ /**
559
+ * @hidden
560
+ */
531
561
  Ungroup = 'ungroup',
562
+ /**
563
+ * @hidden
564
+ */
532
565
  Describe = 'describe',
566
+ /**
567
+ * @hidden
568
+ */
533
569
  Relate = 'relate',
570
+ /**
571
+ * @hidden
572
+ */
534
573
  CustomizeHeadlines = 'customizeHeadlines',
535
574
  /**
536
575
  * @hidden
537
576
  */
538
577
  PinboardInfo = 'pinboardInfo',
539
578
  LiveboardInfo = 'pinboardInfo',
579
+ /**
580
+ * @hidden
581
+ */
540
582
  SendAnswerFeedback = 'sendFeedback',
583
+ /**
584
+ * @hidden
585
+ */
541
586
  DownloadEmbraceQueries = 'downloadEmbraceQueries',
542
587
  Pin = 'pin',
588
+ /**
589
+ * @hidden
590
+ */
543
591
  AnalysisInfo = 'analysisInfo',
544
592
  Subscription = 'subscription',
545
593
  Explore = 'explore',
@@ -547,16 +595,18 @@ export enum Action {
547
595
  DrillExclude = 'context-menu-item-exclude',
548
596
  CopyToClipboard = 'context-menu-item-copy-to-clipboard',
549
597
  CopyAndEdit = 'context-menu-item-copy-and-edit',
598
+ /**
599
+ * @hidden
600
+ */
550
601
  DrillEdit = 'context-menu-item-edit',
551
602
  EditMeasure = 'context-menu-item-edit-measure',
552
603
  Separator = 'context-menu-item-separator',
604
+ /**
605
+ * @hidden
606
+ */
553
607
  DrillDown = 'DRILL',
554
608
  RequestAccess = 'requestAccess',
555
609
  QueryDetailsButtons = 'queryDetailsButtons',
556
- /**
557
- * @version SDK: 1.9.0 | ThoughtSpot: 8.1.0.cl
558
- */
559
- Monitor = 'createMonitor',
560
610
  /**
561
611
  * @version SDK: 1.9.0 | ThoughtSpot: 8.1.0.cl
562
612
  */
@@ -573,6 +623,10 @@ export enum Action {
573
623
  * @version SDK: 1.9.0 | ThoughtSpot: 8.1.0.cl
574
624
  */
575
625
  EditDetails = 'editDetails',
626
+ /**
627
+ * @version SDK: 1.11.0 | ThoughtSpot: 8.3.0.cl
628
+ */
629
+ CreateMonitor = 'createMonitor',
576
630
  }
577
631
 
578
632
  export interface SessionInterface {
package/src/utils.ts CHANGED
@@ -135,3 +135,12 @@ export const getOffsetTop = (element: any) => {
135
135
  const rect = element.getBoundingClientRect();
136
136
  return rect.top + window.scrollY;
137
137
  };
138
+
139
+ export const setAttributes = (
140
+ element: HTMLElement,
141
+ attributes: { [key: string]: string | number | boolean },
142
+ ): void => {
143
+ Object.keys(attributes).forEach((key) => {
144
+ element.setAttribute(key, attributes[key].toString());
145
+ });
146
+ };