• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

thoughtspot / visual-embed-sdk / #2964

10 Dec 2025 05:59AM UTC coverage: 94.22% (-0.2%) from 94.432%
#2964

Pull #375

ruchI9897
resolved cloude comments
Pull Request #375: added check on margin value

1371 of 1540 branches covered (89.03%)

Branch coverage included in aggregate %.

16 of 20 new or added lines in 3 files covered. (80.0%)

5 existing lines in 1 file now uncovered.

3210 of 3322 relevant lines covered (96.63%)

106.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.79
/src/embed/app.ts
1
/**
2
 * Copyright (c) 2022
3
 *
4
 * Full application embedding
5
 * https://developers.thoughtspot.com/docs/?pageid=full-embed
6
 * @summary Full app embed
7
 * @module
8
 * @author Ayon Ghosh <ayon.ghosh@thoughtspot.com>
9
 */
10

11
import { logger } from '../utils/logger';
14✔
12
import { calculateVisibleElementData, getQueryParamString, isUndefined, isValidCssMargin } from '../utils';
14✔
13
import {
14✔
14
    Param,
15
    DOMSelector,
16
    HostEvent,
17
    EmbedEvent,
18
    MessagePayload,
19
    AllEmbedViewConfig,
20
} from '../types';
21
import { V1Embed } from './ts-embed';
14✔
22

23
/**
24
 * Pages within the ThoughtSpot app that can be embedded.
25
 */
26

27
export enum Page {
14✔
28
    /**
29
     * Home page
30
     */
31
    Home = 'home',
14✔
32
    /**
33
     * Search page
34
     */
35
    Search = 'search',
14✔
36
    /**
37
     * Saved answers listing page
38
     */
39
    Answers = 'answers',
14✔
40
    /**
41
     * Liveboards listing page
42
     */
43
    Liveboards = 'liveboards',
14✔
44
    /**
45
     * @hidden
46
     */
47
    Pinboards = 'pinboards',
14✔
48
    /**
49
     * Data management page
50
     */
51
    Data = 'data',
14✔
52
    /**
53
     * SpotIQ listing page
54
     */
55
    SpotIQ = 'insights',
14✔
56
    /**
57
     *  Monitor Alerts Page
58
     */
59
    Monitor = 'monitor'
14✔
60
}
61

62
/**
63
 * Define the initial state os column custom group accordions
64
 * in data panel v2.
65
 */
66
export enum DataPanelCustomColumnGroupsAccordionState {
14✔
67
    /**
68
     * Expand all the accordion initially in data panel v2.
69
     */
70
    EXPAND_ALL = 'EXPAND_ALL',
14✔
71
    /**
72
     * Collapse all the accordions initially in data panel v2.
73
     */
74
    COLLAPSE_ALL = 'COLLAPSE_ALL',
14✔
75
    /**
76
     * Expand the first accordion and collapse the rest.
77
     */
78
    EXPAND_FIRST = 'EXPAND_FIRST',
14✔
79
}
80

81
export enum HomePageSearchBarMode {
14✔
82
    OBJECT_SEARCH = 'objectSearch',
14✔
83
    AI_ANSWER = 'aiAnswer',
14✔
84
    NONE = 'none'
14✔
85
}
86

87
/**
88
 * Define the version of the primary navbar
89
 * @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl
90
 */
91
export enum PrimaryNavbarVersion {
14✔
92
    /**
93
     * Sliding (v3) introduces a new left-side navigation hub featuring a tab switcher,
94
     * along with updates to the top navigation bar.
95
     * It serves as the foundational version of the PrimaryNavBar.
96
     */
97
    Sliding = 'v3',
14✔
98
}
99

100
/**
101
 * Define the version of the home page
102
 * @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl
103
 */
104
export enum HomePage {
14✔
105
    /**
106
     * Modular (v2) introduces the updated Modular Home Experience.
107
     * It serves as the foundational version of the home page.
108
     */
109
    Modular = 'v2',
14✔
110
    /**
111
     * ModularWithStylingChanges (v3) introduces Modular Home Experience
112
     * with styling changes.
113
     */
114
    ModularWithStylingChanges = 'v3',
14✔
115
}
116

117
/**
118
 * Define the version of the list page
119
 * @version SDK: 1.40.0 | ThoughtSpot: 10.12.0.cl
120
 */
121
export enum ListPage {
14✔
122
    /**
123
     * List (v2) is the traditional List Experience.
124
     * It serves as the foundational version of the list page.
125
     */
126
    List = 'v2',
14✔
127
    /**
128
     * ListWithUXChanges (v3) introduces the new updated list page with UX changes.
129
     */
130
    ListWithUXChanges = 'v3',
14✔
131
}
132

133
/**
134
 * Define the discovery experience
135
 * @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl
136
 */
137
export interface DiscoveryExperience {
138
    /**
139
     * primaryNavbarVersion determines the version of the navigation version.
140
     */
141
    primaryNavbarVersion?: PrimaryNavbarVersion;
142
    /**
143
     * homePage determines the version of the home page.
144
     */
145
    homePage?: HomePage;
146
    /**
147
     * listPageVersion determines the version of the list page.
148
     */
149
    listPageVersion?: ListPage;
150
}
151

152
/**
153
 * The view configuration for full app embedding.
154
 * @group Embed components
155
 */
156
export interface AppViewConfig extends AllEmbedViewConfig {
157
    /**
158
     * If true, the top navigation bar within the ThoughtSpot app
159
     * is displayed. By default, the navigation bar is hidden.
160
     * This flag also controls the homepage left navigation bar.
161
     *
162
     * Supported embed types: `AppEmbed`
163
     * @default true
164
     * @version SDK: 1.2.0 | ThoughtSpot: 8.4.0.cl
165
     * @example
166
     * ```js
167
     * const embed = new AppEmbed('#tsEmbed', {
168
     *    ... // other embed view config
169
     *    showPrimaryNavbar:true,
170
     * })
171
     * ```
172
     */
173
    showPrimaryNavbar?: boolean;
174
    /**
175
     * Control the visibility of the left navigation bar on the Homepage.
176
     * If showPrimaryNavbar is true, that is, if the Global and Homepage
177
     * nav-bars are visible, this flag will only hide the homepage left nav-bar.
178
     * The showPrimaryNavbar flag takes precedence over the hideHomepageLeftNav.
179
     *
180
     * **Note**: This option does not apply to the classic homepage.
181
     * To access the updated modular homepage, set
182
     * `modularHomeExperience` to `true` (available as Early Access feature in 9.12.5.cl).
183
     *
184
     * Supported embed types: `AppEmbed`
185
     * @default false
186
     * @version SDK: 1.28.0 | ThoughtSpot: 9.12.5.cl
187
     * @example
188
     * ```js
189
     * const embed = new AppEmbed('#tsEmbed', {
190
     *    ... // other embed view config
191
     *    hideHomepageLeftNav : true,
192
     * })
193
     * ```
194
     */
195
    hideHomepageLeftNav?: boolean;
196
    /**
197
     * Control the visibility of the help (?) and profile buttons on the
198
     * Global nav-bar. By default, these buttons are visible on the nav-bar.
199
     *
200
     * Supported embed types: `AppEmbed`
201
     * @default false
202
     * @version SDK: 1.2.0 | ThoughtSpot: 8.4.0.cl
203
     * @example
204
     * ```js
205
     * const embed = new AppEmbed('#tsEmbed', {
206
     *    ... // other embed view config
207
     *    disableProfileAndHelp: true,
208
     * })
209
     * ```
210
     */
211
    disableProfileAndHelp?: boolean;
212
    /**
213
     * @version SDK: 1.36.3 | ThoughtSpot: 10.1.0.cl
214
     * @default true
215
     * Whether the help menu in the top nav bar should be served
216
     * from Pendo or ThoughtSpot's internal help items.
217
     *
218
     * Supported embed types: `AppEmbed`
219
     * @example
220
     * ```js
221
     * const embed = new AppEmbed('#tsEmbed', {
222
     *   ... // other embed view config
223
     *  enablePendoHelp: false,
224
     * });
225
     * ```
226
     */
227
    enablePendoHelp?: boolean
228
    /**
229
     * Control the visibility of the hamburger icon on the top nav bar
230
     * available when new navigation V3 is enabled.
231
     *
232
     * Supported embed types: `AppEmbed`
233
     * @default false
234
     * @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl
235
     * @example
236
     * ```js
237
     * const embed = new AppEmbed('#tsEmbed', {
238
     *    ... // other embed view config
239
     *    hideHamburger : true,
240
     * })
241
     * ```
242
     */
243
    hideHamburger?: boolean;
244
    /**
245
     * Control the visibility of the Eureka search on the top nav bar
246
     * this will control for both new V2 and new navigation V3.
247
     *
248
     * Supported embed types: `AppEmbed`
249
     * @default true
250
     * @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl
251
     * @example
252
     * ```js
253
     * const embed = new AppEmbed('#tsEmbed', {
254
     *    ... // other embed view config
255
     *    hideObjectSearch: false,
256
     * })
257
     * ```
258
     */
259
    hideObjectSearch?: boolean;
260
    /**
261
     * Control the visibility of the notification on the top nav bar V3,
262
     * available when new navigation V3 is enabled.
263
     *
264
     * Supported embed types: `AppEmbed`
265
     * @default true
266
     * @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl
267
     * @example
268
     * ```js
269
     * const embed = new AppEmbed('#tsEmbed', {
270
     *    ... // other embed view config
271
     *    hideNotification: false,
272
     * })
273
     * ```
274
     */
275
    hideNotification?: boolean;
276
    /**
277
     * Control the visibility of the application switcher button on the nav-bar.
278
     * By default, the application switcher is shown.
279
     *
280
     * **Note**: This option does not apply to the classic homepage.
281
     * To access the updated modular homepage, set
282
     * `modularHomeExperience` to `true` (available as Early Access feature in 9.12.5.cl).
283
     *
284
     * Supported embed types: `AppEmbed`
285
     * @version SDK: 1.28.0 | ThoughtSpot: 9.12.5.cl
286
     * @default false
287
     * @example
288
     * ```js
289
     * const embed = new AppEmbed('#tsEmbed', {
290
     *    ... // other embed view config
291
     *    hideApplicationSwitcher : true,
292
     * })
293
     * ```
294
     */
295
    hideApplicationSwitcher?: boolean;
296
    /**
297
     * Control the visibility of the Org switcher button on the nav-bar.
298
     * By default, the Org switcher button is shown.
299
     *
300
     * **Note**: This option does not apply to the classic homepage.
301
     * To access the updated modular homepage, set
302
     * `modularHomeExperience` to `true` (available as Early Access feature in 9.12.5.cl).
303
     *
304
     * Supported embed types: `AppEmbed`
305
     * @version SDK: 1.28.0 | ThoughtSpot: 9.12.5.cl
306
     * @default true
307
     * @example
308
     * ```js
309
     * const embed = new AppEmbed('#tsEmbed', {
310
     *    ... // other embed view config
311
     *    hideOrgSwitcher : true,
312
     * })
313
     * ```
314
     */
315
    hideOrgSwitcher?: boolean;
316
    /**
317
     * A URL path to the embedded application page
318
     * If both path and pageId attributes are defined, the path definition
319
     * takes precedence. This is the path post the `#/` in the URL of the standalone
320
     * ThoughtSpot app. Use this to open the embedded view to a specific path.
321
     *
322
     * For eg, if you want the component to open to a specific Liveboard
323
     * you could set the path to `pinboard/<liveboardId>/tab/<tabId>`.
324
     *
325
     * Supported embed types: `AppEmbed`
326
     * @version SDK: 1.1.0 | ThoughtSpot: 9.4.0.cl
327
     * @example
328
     * ```js
329
     * const embed = new AppEmbed('#tsEmbed', {
330
     *    ... // other embed view config
331
     *    path:"pinboard/1234/tab/7464",
332
     * })
333
     * ```
334
     */
335
    path?: string;
336
    /**
337
     * The application page to set as the start page
338
     * in the embedded view.
339
     *
340
     * Use this to open to particular page in the app. To open to a specific
341
     * path within the app, use the `path` attribute which is more flexible.
342
     *
343
     * Supported embed types: `AppEmbed`
344
     * @version SDK: 1.1.0 | ThoughtSpot: 9.4.0.cl
345
     * @example
346
     * ```js
347
     * const embed = new AppEmbed('#tsEmbed', {
348
     *    ... // other embed view config
349
     *    pageId : Page.Answers | Page.Data,
350
     * })
351
     * ```
352
     */
353
    pageId?: Page;
354
    /**
355
     * This puts a filter tag on the application. All metadata lists in the
356
     * application, such as Liveboards and answers, would be filtered by this
357
     * tag.
358
     *
359
     * Supported embed types: `AppEmbed`
360
     * @version SDK: 1.1.0 | ThoughtSpot: 9.4.0.cl
361
     * @example
362
     * ```js
363
     * const embed = new AppEmbed('#tsEmbed', {
364
     *    ... // other embed view config
365
     *    tag:'value',
366
     * })
367
     * ```
368
     */
369
    tag?: string;
370
    /**
371
     * Hide tag filter chips that appear when content is filtered by tags.
372
     * When enabled, this automatically:
373
     * - Hides tag filter indicators/chips from the UI
374
     *
375
     * This provides a clean interface without tag-related UI elements.
376
     *
377
     * Supported embed types: `AppEmbed`
378
     * @version SDK: 1.44.0 | ThoughtSpot: 10.15.0.cl
379
     * @example
380
     * ```js
381
     * // Simple usage - automatically hides all tag-related UI
382
     * const embed = new AppEmbed('#tsEmbed', {
383
     *    ... // other embed view config
384
     *    tag: 'Some Tag',
385
     *    hideTagFilterChips: true, // This is all you need!
386
     * });
387
     * ```
388
     */
389
    hideTagFilterChips?: boolean;
390
    /**
391
     * The array of GUIDs to be hidden
392
     *
393
     * Supported embed types: `AppEmbed`
394
     * @version SDK: 1.11.0 | ThoughtSpot: 8.3.0.cl, 8.4.1-sw
395
     * @example
396
     * ```js
397
     * const embed = new AppEmbed('#tsEmbed', {
398
     *    ... // other embed view config
399
     *    hideObjects: [
400
     *       '430496d6-6903-4601-937e-2c691821af3c',
401
     *       'f547ec54-2a37-4516-a222-2b06719af726'
402
     *     ]
403
     * })
404
     * ```
405
     */
406
    hideObjects?: string[];
407
    /**
408
     * Render liveboards using the new v2 rendering mode
409
     * This is a transient flag which is primarily meant for internal use
410
     *
411
     * Supported embed types: `AppEmbed`
412
     * @version SDK: 1.11.0 | ThoughtSpot: 8.3.0.cl, 8.4.1-sw
413
     * @hidden
414
     */
415
    liveboardV2?: boolean;
416
    /**
417
     * If set to true, the Search Assist feature is enabled.
418
     *
419
     * Supported embed types: `AppEmbed`
420
     * @default true
421
     * @version SDK: 1.13.0 | ThoughtSpot: 8.5.0.cl, 8.8.1-sw
422
     * @example
423
     * ```js
424
     * const embed = new AppEmbed('#tsEmbed', {
425
     *    ... // other embed view config
426
     *    enableSearchAssist: true,
427
     * })
428
     * ```
429
     */
430
    enableSearchAssist?: boolean;
431
    /**
432
     * If set to true, the Liveboard container dynamically resizes
433
     * according to the height of the Liveboard.
434
     *
435
     * **Note**: Using fullHeight loads all visualizations
436
     * on the Liveboard simultaneously, which results in
437
     * multiple warehouse queries and potentially a
438
     * longer wait for the topmost visualizations to
439
     * display on the screen. Setting fullHeight to
440
     * `false` fetches visualizations incrementally as
441
     * users scroll the page to view the charts and tables.
442
     *
443
     * Supported embed types: `AppEmbed`
444
     * @version SDK: 1.21.0 | ThoughtSpot: 9.4.0.cl, 9.4.0-sw
445
     * @example
446
     * ```js
447
     * const embed = new AppEmbed('#tsEmbed', {
448
     *    ... // other embed view config
449
     *    fullHeight: true,
450
     * })
451
     * ```
452
     */
453
    fullHeight?: boolean;
454
    /**
455
     * Flag to control new Modular Home experience.
456
     *
457
     * Supported embed types: `AppEmbed`
458
     * @default false
459
     * @version SDK: 1.28.0 | ThoughtSpot: 9.12.5.cl
460
     * @example
461
     * ```js
462
     * const embed = new AppEmbed('#tsEmbed', {
463
     *    ... // other embed view config
464
     *    modularHomeExperience : true,
465
     * })
466
     * ```
467
     */
468
    modularHomeExperience?: boolean;
469
    /**
470
     * To configure the top-left navigation and home page experience
471
     *
472
     * Supported embed types: `AppEmbed`
473
     * @default false
474
     * @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl
475
     * @example
476
     * ```js
477
     * const embed = new AppEmbed('#tsEmbed', {
478
     *    ... // other embed view config
479
     *    discoveryExperience : {
480
     *      primaryNavbarVersion: PrimaryNavbarVersion.Sliding,
481
     *      homePage: HomePage.Modular,
482
     *    },
483
     * })
484
     * ```
485
     */
486
    discoveryExperience?: DiscoveryExperience;
487
    /**
488
     * To set the initial state of the search bar in case of saved-answers.
489
     * @version SDK: 1.32.0 | ThoughtSpot: 10.0.0.cl
490
     * @default false
491
     * @deprecated Use {@link collapseSearchBar} instead
492
     */
493
    collapseSearchBarInitially?: boolean;
494
    /**
495
     * This controls the initial behaviour of custom column groups accordion.
496
     * It takes DataPanelCustomColumnGroupsAccordionState enum values as input.
497
     * List of different enum values:-
498
     * - EXPAND_ALL: Expand all the accordion initially in data panel v2.
499
     * - COLLAPSE_ALL: Collapse all the accordions initially in data panel v2.
500
     * - EXPAND_FIRST: Expand the first accordion and collapse the rest.
501
     *
502
     * Supported embed types: `AppEmbed`
503
     * @version SDK: 1.32.0 | ThoughtSpot: 10.0.0.cl
504
     * @default DataPanelCustomColumnGroupsAccordionState.EXPAND_ALL
505
     * @example
506
     * ```js
507
     * const embed = new AppEmbed('#embed', {
508
     *   ... // other app view config
509
     *   dataPanelCustomGroupsAccordionInitialState:
510
     *      DataPanelCustomColumnGroupsAccordionState.EXPAND_ALL,
511
     * });
512
     * ```
513
     */
514
    dataPanelCustomGroupsAccordionInitialState?: DataPanelCustomColumnGroupsAccordionState;
515
    /**
516
     * Flag to use home page search bar mode
517
     *
518
     * Supported embed types: `AppEmbed`
519
     * @version SDK : 1.33.0 | ThoughtSpot: 10.3.0.cl
520
     */
521
    homePageSearchBarMode?: HomePageSearchBarMode;
522
    /**
523
     * This flag is used to enable unified search experience for full app embed.
524
     *
525
     * Supported embed types: `AppEmbed`
526
     * @version SDK: 1.34.0 | ThoughtSpot:10.5.0.cl
527
     * @default true
528
     * @example
529
     * ```js
530
     * const embed = new AppEmbed('#tsEmbed', {
531
     *    ... // other embed view config
532
     *    isUnifiedSearchExperienceEnabled: true,
533
     * })
534
     * ```
535
     */
536
    isUnifiedSearchExperienceEnabled?: boolean;
537

538
    /**
539
     * This flag is used to enable/disable the styling and grouping in a Liveboard
540
     *
541
     * Supported embed types: `AppEmbed`, `LiveboardEmbed`
542
     * @type {boolean}
543
     * @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl
544
     * @example
545
     * ```js
546
     * // Replace <EmbedComponent> with embed component name. For example, AppEmbed or LiveboardEmbed
547
     * const embed = new <EmbedComponent>('#tsEmbed', {
548
     *    ... // other embed view config
549
     *    isLiveboardStylingAndGroupingEnabled: true,
550
     * })
551
     * ```
552
     */
553
    isLiveboardStylingAndGroupingEnabled?: boolean;
554

555
    /**
556
     * This flag is used to enable/disable the png embedding of liveboard in scheduled mails
557
     *
558
     * Supported embed types: `AppEmbed`, `LiveboardEmbed`
559
     * @type {boolean}
560
     * @version SDK: 1.42.0 | ThoughtSpot: 10.14.0.cl
561
     * @example
562
     * ```js
563
     * // Replace <EmbedComponent> with embed component name. For example, AppEmbed or LiveboardEmbed
564
     * const embed = new <EmbedComponent>('#tsEmbed', {
565
     *    ... // other embed view config
566
     *    isPNGInScheduledEmailsEnabled: true,
567
     * })
568
     * ```
569
     */
570
    isPNGInScheduledEmailsEnabled?: boolean;
571

572
    /**
573
     * This flag is used to enable the full height lazy load data.
574
     *
575
     * @example
576
     * ```js
577
     * const embed = new AppEmbed('#embed-container', {
578
     *    // ...other options
579
     *    fullHeight: true,
580
     *    lazyLoadingForFullHeight: true,
581
     * })
582
     * ```
583
     *
584
     * @type {boolean}
585
     * @default false
586
     * @version SDK: 1.40.0 | ThoughtSpot:10.12.0.cl
587
     */
588
    lazyLoadingForFullHeight?: boolean;
589

590
    /**
591
     * The margin to be used for lazy loading.
592
     *
593
     * For example, if the margin is set to '10px',
594
     * the visualization will be loaded 10px before the its top edge is visible in the
595
     * viewport.
596
     *
597
     * The format is similar to CSS margin.
598
     *
599
     * @example
600
     * ```js
601
     * const embed = new AppEmbed('#embed-container', {
602
     *    // ...other options
603
     *    fullHeight: true,
604
     *    lazyLoadingForFullHeight: true,
605
     *   // Using 0px, the visualization will be only loaded when its visible in the viewport.
606
     *    lazyLoadingMargin: '0px',
607
     * })
608
     * ```
609
     * @type {string}
610
     * @version SDK: 1.40.0 | ThoughtSpot:10.12.0.cl
611
     */
612
    lazyLoadingMargin?: string;
613

614
    /**
615
     * updatedSpotterChatPrompt : Controls the updated spotter chat prompt.
616
     *
617
     * Supported embed types: `AppEmbed`
618
     * @default false
619
     * @example
620
     * ```js
621
     * const embed = new AppEmbed('#tsEmbed', {
622
     *    ... //other embed view config
623
     *    updatedSpotterChatPrompt : true,
624
     * })
625
     * ```
626
     * @version SDK: 1.45.0 | ThoughtSpot: 26.2.0.cl
627
     */
628
    updatedSpotterChatPrompt?: boolean;
629
    /**
630
     * This is the minimum height (in pixels) for a full-height App.
631
     * Setting this height helps resolve issues with empty Apps and
632
     * other screens navigable from an App.
633
     *
634
     * @version SDK: 1.44.2 | ThoughtSpot: 26.0.2.cl
635
     * @default 500
636
     * @example
637
     * ```js
638
     * const embed = new AppEmbed('#embed', {
639
     *   ... // other app view config
640
     *   fullHeight: true,
641
     *   minimumHeight: 600,
642
     * });
643
     * ```
644
     */
645
    minimumHeight?: number;
646
}
647

648
/**
649
 * Embeds full ThoughtSpot experience in a host application.
650
 * @group Embed components
651
 */
652
export class AppEmbed extends V1Embed {
14✔
653
    protected viewConfig: AppViewConfig;
654

655
    private defaultHeight = 500;
142✔
656

657

658
    constructor(domSelector: DOMSelector, viewConfig: AppViewConfig) {
659
        viewConfig.embedComponentType = 'AppEmbed';
142✔
660
        super(domSelector, viewConfig);
142✔
661
        if (this.viewConfig.fullHeight === true) {
142✔
662
            this.on(EmbedEvent.RouteChange, this.setIframeHeightForNonEmbedLiveboard);
17✔
663
            this.on(EmbedEvent.EmbedHeight, this.updateIFrameHeight);
17✔
664
            this.on(EmbedEvent.EmbedIframeCenter, this.embedIframeCenter);
17✔
665
            this.on(EmbedEvent.RequestVisibleEmbedCoordinates, this.requestVisibleEmbedCoordinatesHandler);
17✔
666
        }
667
    }
668

669
    /**
670
     * Constructs a map of parameters to be passed on to the
671
     * embedded Liveboard or visualization.
672
     */
673
    protected getEmbedParams() {
674
        const {
675
            tag,
676
            hideTagFilterChips,
677
            hideObjects,
678
            liveboardV2,
679
            showPrimaryNavbar,
680
            disableProfileAndHelp,
681
            hideHamburger,
682
            hideObjectSearch,
683
            hideNotification,
684
            hideApplicationSwitcher,
685
            hideOrgSwitcher,
686
            enableSearchAssist,
687
            fullHeight,
688
            dataPanelV2 = true,
140✔
689
            hideLiveboardHeader = false,
141✔
690
            showLiveboardTitle = true,
141✔
691
            showLiveboardDescription = true,
141✔
692
            showMaskedFilterChip = false,
139✔
693
            isLiveboardMasterpiecesEnabled = false,
139✔
694
            hideHomepageLeftNav = false,
140✔
695
            modularHomeExperience = false,
132✔
696
            isLiveboardHeaderSticky = true,
140✔
697
            enableAskSage,
698
            collapseSearchBarInitially = false,
141✔
699
            enable2ColumnLayout,
700
            enableCustomColumnGroups = false,
141✔
701
            dataPanelCustomGroupsAccordionInitialState = DataPanelCustomColumnGroupsAccordionState.EXPAND_ALL,
140✔
702
            collapseSearchBar = true,
141✔
703
            isLiveboardCompactHeaderEnabled = false,
140✔
704
            showLiveboardVerifiedBadge = true,
140✔
705
            showLiveboardReverifyBanner = true,
140✔
706
            hideIrrelevantChipsInLiveboardTabs = false,
140✔
707
            isEnhancedFilterInteractivityEnabled = false,
140✔
708
            homePageSearchBarMode,
709
            isUnifiedSearchExperienceEnabled = true,
140✔
710
            enablePendoHelp = true,
140✔
711
            discoveryExperience,
712
            coverAndFilterOptionInPDF = false,
140✔
713
            liveboardXLSXCSVDownload,
714
            isLiveboardStylingAndGroupingEnabled,
715
            isPNGInScheduledEmailsEnabled = false,
140✔
716
            isCentralizedLiveboardFilterUXEnabled = false,
139✔
717
            isLinkParametersEnabled,
718
            updatedSpotterChatPrompt,
719
            minimumHeight,
720
        } = this.viewConfig;
141✔
721

722
        let params: any = {};
141✔
723
        params[Param.PrimaryNavHidden] = !showPrimaryNavbar;
141✔
724
        params[Param.HideProfleAndHelp] = !!disableProfileAndHelp;
141✔
725
        params[Param.HideApplicationSwitcher] = !!hideApplicationSwitcher;
141✔
726
        params[Param.HideOrgSwitcher] = !!hideOrgSwitcher;
141✔
727
        params[Param.HideLiveboardHeader] = hideLiveboardHeader;
141✔
728
        params[Param.ShowLiveboardTitle] = showLiveboardTitle;
141✔
729
        params[Param.ShowLiveboardDescription] = !!showLiveboardDescription;
141✔
730
        params[Param.ShowMaskedFilterChip] = showMaskedFilterChip;
141✔
731
        params[Param.IsLiveboardMasterpiecesEnabled] = isLiveboardMasterpiecesEnabled;
141✔
732
        params[Param.LiveboardHeaderSticky] = isLiveboardHeaderSticky;
141✔
733
        params[Param.IsFullAppEmbed] = true;
141✔
734
        params[Param.LiveboardHeaderV2] = isLiveboardCompactHeaderEnabled;
141✔
735
        params[Param.IsEnhancedFilterInteractivityEnabled] = isEnhancedFilterInteractivityEnabled;
141✔
736
        params[Param.ShowLiveboardVerifiedBadge] = showLiveboardVerifiedBadge;
141✔
737
        params[Param.ShowLiveboardReverifyBanner] = showLiveboardReverifyBanner;
141✔
738
        params[Param.HideIrrelevantFiltersInTab] = hideIrrelevantChipsInLiveboardTabs;
141✔
739
        params[Param.IsUnifiedSearchExperienceEnabled] = isUnifiedSearchExperienceEnabled;
141✔
740
        params[Param.CoverAndFilterOptionInPDF] = !!coverAndFilterOptionInPDF;
141✔
741

742
        params = this.getBaseQueryParams(params);
141✔
743

744
        if (!isUndefined(updatedSpotterChatPrompt)) {
141✔
745
            params[Param.UpdatedSpotterChatPrompt] = !!updatedSpotterChatPrompt;
2✔
746
        }
747

748
        if (hideObjectSearch) {
141✔
749
            params[Param.HideObjectSearch] = !!hideObjectSearch;
1✔
750
        }
751

752
        if (hideHamburger) {
141✔
753
            params[Param.HideHamburger] = !!hideHamburger;
1✔
754
        }
755

756
        if (hideNotification) {
141✔
757
            params[Param.HideNotification] = !!hideNotification;
1✔
758
        }
759

760
        if (fullHeight === true) {
141✔
761
            params[Param.fullHeight] = true;
18✔
762
            if (this.viewConfig.lazyLoadingForFullHeight) {
18✔
763
                params[Param.IsLazyLoadingForEmbedEnabled] = true;
9✔
764
                if (this.viewConfig.lazyLoadingMargin === undefined || !isValidCssMargin(this.viewConfig.lazyLoadingMargin)) {
9!
NEW
765
                    logger.error('Please provide a valid lazyLoadingMargin value (e.g., "10px"). Defaulting to "0px".');
×
NEW
766
                    params[Param.RootMarginForLazyLoad] = '0px';
×
767
                } else {
768
                    params[Param.RootMarginForLazyLoad] = this.viewConfig.lazyLoadingMargin;
9✔
769
                }
770
            }
771
        }
772

773
        if (tag) {
141✔
774
            params[Param.Tag] = tag;
1✔
775
        }
776
        if (hideObjects && hideObjects.length) {
141!
777
            params[Param.HideObjects] = JSON.stringify(hideObjects);
×
778
        }
779
        if (liveboardV2 !== undefined) {
141!
780
            params[Param.LiveboardV2Enabled] = liveboardV2;
×
781
        }
782

783
        if (enableSearchAssist !== undefined) {
141✔
784
            params[Param.EnableSearchAssist] = enableSearchAssist;
1✔
785
        }
786

787
        if (enable2ColumnLayout !== undefined) {
141✔
788
            params[Param.Enable2ColumnLayout] = enable2ColumnLayout;
1✔
789
        }
790

791
        if (enableAskSage) {
141✔
792
            params[Param.enableAskSage] = enableAskSage;
1✔
793
        }
794

795
        if (homePageSearchBarMode) {
141✔
796
            params[Param.HomePageSearchBarMode] = homePageSearchBarMode;
3✔
797
        }
798

799
        if (enablePendoHelp !== undefined) {
141✔
800
            params[Param.EnablePendoHelp] = enablePendoHelp;
141✔
801
        }
802

803
        if (isLiveboardStylingAndGroupingEnabled !== undefined) {
141✔
804
            params[Param.IsLiveboardStylingAndGroupingEnabled] = isLiveboardStylingAndGroupingEnabled;
1✔
805
        }
806

807
        if (liveboardXLSXCSVDownload !== undefined) {
141✔
808
            params[Param.LiveboardXLSXCSVDownload] = !!liveboardXLSXCSVDownload;
2✔
809
        }
810

811
        if (isPNGInScheduledEmailsEnabled !== undefined) {
141✔
812
            params[Param.isPNGInScheduledEmailsEnabled] = isPNGInScheduledEmailsEnabled;
141✔
813
        }
814

815
        if (hideTagFilterChips !== undefined) {
141✔
816
            params[Param.HideTagFilterChips] = hideTagFilterChips;
2✔
817
        }
818

819
        if (isLinkParametersEnabled !== undefined) {
141✔
820
            params[Param.isLinkParametersEnabled] = isLinkParametersEnabled;
2✔
821
        }
822

823
        if (isCentralizedLiveboardFilterUXEnabled != undefined) {
141✔
824
            params[
141✔
825
                Param.isCentralizedLiveboardFilterUXEnabled
826
            ] = isCentralizedLiveboardFilterUXEnabled;
827
        }
828

829
        this.defaultHeight = minimumHeight || this.defaultHeight;
141✔
830

831
        params[Param.DataPanelV2Enabled] = dataPanelV2;
141✔
832
        params[Param.HideHomepageLeftNav] = hideHomepageLeftNav;
141✔
833
        params[Param.ModularHomeExperienceEnabled] = modularHomeExperience;
141✔
834
        params[Param.CollapseSearchBarInitially] = collapseSearchBarInitially || collapseSearchBar;
141✔
835
        params[Param.EnableCustomColumnGroups] = enableCustomColumnGroups;
141✔
836
        if (dataPanelCustomGroupsAccordionInitialState
141✔
837
            === DataPanelCustomColumnGroupsAccordionState.COLLAPSE_ALL
838
            || dataPanelCustomGroupsAccordionInitialState
839
            === DataPanelCustomColumnGroupsAccordionState.EXPAND_FIRST
840
        ) {
841

842
            params[
1✔
843
                Param.DataPanelCustomGroupsAccordionInitialState
844
            ] = dataPanelCustomGroupsAccordionInitialState;
845
        } else {
846

847
            params[Param.DataPanelCustomGroupsAccordionInitialState] = DataPanelCustomColumnGroupsAccordionState.EXPAND_ALL;
140✔
848
        }
849

850
        // Set navigation to v2 by default to avoid problems like the app
851
        // switcher (9-dot menu) not showing when v3 navigation is turned on
852
        // at the cluster level.
853
        // To use v3 navigation, we must manually set the discoveryExperience
854
        // settings.
855
        params[Param.NavigationVersion] = 'v2';
141✔
856
        // Set homePageVersion to v2 by default to reset the LD flag value
857
        // for the homepageVersion.
858
        params[Param.HomepageVersion] = 'v2';
141✔
859
        // Set listpageVersion to v2 by default to reset the LD flag value
860
        // for the listpageVersion.
861
        params[Param.ListPageVersion] = ListPage.List;
141✔
862
        if (discoveryExperience) {
141✔
863
            // primaryNavbarVersion v3 will enabled the new left navigation
864
            if (discoveryExperience.primaryNavbarVersion === PrimaryNavbarVersion.Sliding) {
8✔
865
                params[Param.NavigationVersion] = discoveryExperience.primaryNavbarVersion;
5✔
866
                // Enable the modularHomeExperience when Sliding is enabled.
867
                params[Param.ModularHomeExperienceEnabled] = true;
5✔
868
            }
869

870
            // homePage v2 will enable the modular home page
871
            // and it will override the modularHomeExperience value
872
            if (discoveryExperience.homePage === HomePage.Modular) {
8✔
873
                params[Param.ModularHomeExperienceEnabled] = true;
4✔
874
            }
875

876
            // ModularWithStylingChanges (v3) introduces the styling changes
877
            // to the Modular Homepage.
878
            // v3 will be the base version of homePageVersion.
879
            if (discoveryExperience.homePage === HomePage.ModularWithStylingChanges) {
8✔
880
                params[Param.HomepageVersion] = HomePage.ModularWithStylingChanges;
2✔
881
            }
882

883
            // listPageVersion v3 will enable the new list page
884
            if (discoveryExperience.listPageVersion === ListPage.ListWithUXChanges) {
8✔
885
                params[Param.ListPageVersion] = discoveryExperience.listPageVersion;
2✔
886
            }
887
        }
888

889
        const queryParams = getQueryParamString(params, true);
141✔
890

891
        return queryParams;
141✔
892
    }
893

894
    private sendFullHeightLazyLoadData = () => {
142✔
895
        const data = calculateVisibleElementData(this.iFrame);
5✔
896
        this.trigger(HostEvent.VisibleEmbedCoordinates, data);
5✔
897
    }
898

899
    /**
900
     * This is a handler for the RequestVisibleEmbedCoordinates event.
901
     * It is used to send the visible coordinates data to the host application.
902
     * @param data The event payload
903
     * @param responder The responder function
904
     */
905
    private requestVisibleEmbedCoordinatesHandler = (data: MessagePayload, responder: any) => {
142✔
906
        logger.info('Sending RequestVisibleEmbedCoordinates', data);
1✔
907
        const visibleCoordinatesData = calculateVisibleElementData(this.iFrame);
1✔
908
        responder({ type: EmbedEvent.RequestVisibleEmbedCoordinates, data: visibleCoordinatesData });
1✔
909
    }
910

911
    /**
912
     * Constructs the URL of the ThoughtSpot app page to be rendered.
913
     * @param pageId The ID of the page to be embedded.
914
     */
915
    public getIFrameSrc(): string {
916
        const { pageId, path, modularHomeExperience } = this.viewConfig;
141✔
917
        const pageRoute = this.formatPath(path) || this.getPageRoute(pageId, modularHomeExperience);
141✔
918
        let url = `${this.getRootIframeSrc()}/${pageRoute}`;
141✔
919

920
        const tsPostHashParams = this.getThoughtSpotPostUrlParams();
141✔
921
        url = `${url}${tsPostHashParams}`;
141✔
922

923
        return url;
141✔
924
    }
925

926
    /**
927
     * Set the iframe height as per the computed height received
928
     * from the ThoughtSpot app.
929
     * @param data The event payload
930
     */
931
    protected updateIFrameHeight = (data: MessagePayload) => {
142✔
932
        this.setIFrameHeight(Math.max(data.data, this.defaultHeight));
3✔
933
        this.sendFullHeightLazyLoadData();
3✔
934
    };
935

936
    private embedIframeCenter = (data: MessagePayload, responder: any) => {
142✔
937
        const obj = this.getIframeCenter();
2✔
938
        responder({ type: EmbedEvent.EmbedIframeCenter, data: obj });
2✔
939
    };
940

941
    private setIframeHeightForNonEmbedLiveboard = (data: MessagePayload) => {
142✔
942
        const { height: frameHeight } = this.viewConfig.frameParams || {};
5!
943

944
        const liveboardRelatedRoutes = [
5✔
945
            '/pinboard/',
946
            '/insights/pinboard/',
947
            '/schedules/',
948
            '/embed/viz/',
949
            '/embed/insights/viz/',
950
            '/liveboard/',
951
            '/insights/liveboard/',
952
            '/tsl-editor/PINBOARD_ANSWER_BOOK/',
953
            '/import-tsl/PINBOARD_ANSWER_BOOK/',
954
        ];
955

956
        if (liveboardRelatedRoutes.some((path) => data.data.currentPath.startsWith(path))) {
36✔
957
            // Ignore the height reset of the frame, if the navigation is
958
            // only within the liveboard page.
959
            return;
2✔
960
        }
961
        this.setIFrameHeight(frameHeight || this.defaultHeight);
3!
962
    };
963

964
    /**
965
     * Gets the ThoughtSpot route of the page for a particular page ID.
966
     * @param pageId The identifier for a page in the ThoughtSpot app.
967
     * @param modularHomeExperience
968
     */
969
    private getPageRoute(pageId: Page, modularHomeExperience = false) {
131✔
970
        switch (pageId) {
140✔
971
            case Page.Search:
972
                return 'answer';
2✔
973
            case Page.Answers:
974
                return modularHomeExperience ? 'home/answers' : 'answers';
2✔
975
            case Page.Liveboards:
976
                return modularHomeExperience ? 'home/liveboards' : 'pinboards';
2✔
977
            case Page.Pinboards:
978
                return modularHomeExperience ? 'home/liveboards' : 'pinboards';
2✔
979
            case Page.Data:
980
                return 'data/tables';
2✔
981
            case Page.SpotIQ:
982
                return modularHomeExperience ? 'home/spotiq-analysis' : 'insights/results';
2✔
983
            case Page.Monitor:
984
                return modularHomeExperience ? 'home/monitor-alerts' : 'insights/monitor-alerts';
2✔
985
            case Page.Home:
986
            default:
987
                return 'home';
126✔
988
        }
989
    }
990

991
    /**
992
     * Formats the path provided by the user.
993
     * @param path The URL path.
994
     * @returns The URL path that the embedded app understands.
995
     */
996
    private formatPath(path: string) {
997
        if (!path) {
141✔
998
            return null;
140✔
999
        }
1000

1001
        // remove leading slash
1002
        if (path.indexOf('/') === 0) {
1!
1003
            return path.substring(1);
×
1004
        }
1005

1006
        return path;
1✔
1007
    }
1008

1009
    /**
1010
     * Navigate to particular page for app embed. eg:answers/pinboards/home
1011
     * This is used for embedding answers, pinboards, visualizations and full application
1012
     * only.
1013
     * @param path string | number The string, set to iframe src and navigate to new page
1014
     * eg: appEmbed.navigateToPage('pinboards')
1015
     * When used with `noReload` (default: true) this can also be a number
1016
     * like 1/-1 to go forward/back.
1017
     * @param noReload boolean Trigger the navigation without reloading the page
1018
     * @version SDK: 1.12.0 | ThoughtSpot: 8.4.0.cl, 8.4.1-sw
1019
     */
1020
    public navigateToPage(path: string | number, noReload = false): void {
3✔
1021
        if (!this.iFrame) {
8✔
1022
            logger.log('Please call render before invoking this method');
2✔
1023
            return;
2✔
1024
        }
1025
        if (noReload) {
6✔
1026
            this.trigger(HostEvent.Navigate, path);
2✔
1027
        } else {
1028
            if (typeof path !== 'string') {
4✔
1029
                logger.warn('Path can only by a string when triggered without noReload');
1✔
1030
                return;
1✔
1031
            }
1032
            const iframeSrc = this.iFrame.src;
3✔
1033
            const embedPath = '#/embed';
3✔
1034
            const currentPath = iframeSrc.includes(embedPath) ? embedPath : '#';
3!
1035
            this.iFrame.src = `${iframeSrc.split(currentPath)[0]}${currentPath}/${path.replace(
3✔
1036
                /^\/?#?\//,
1037
                '',
1038
            )}`;
1039
        }
1040
    }
1041

1042
    /**
1043
     * Destroys the ThoughtSpot embed, and remove any nodes from the DOM.
1044
     * @version SDK: 1.39.0 | ThoughtSpot: 10.10.0.cl
1045
     */
1046
    public destroy() {
1047
        super.destroy();
10✔
1048
        this.unregisterLazyLoadEvents();
10✔
1049
    }
1050

1051
    private postRender() {
1052
        this.registerLazyLoadEvents();
140✔
1053
    }
1054

1055
    private registerLazyLoadEvents() {
1056
        if (this.viewConfig.fullHeight && this.viewConfig.lazyLoadingForFullHeight) {
140✔
1057
            // TODO: Use passive: true, install modernizr to check for passive
1058
            window.addEventListener('resize', this.sendFullHeightLazyLoadData);
8✔
1059
            window.addEventListener('scroll', this.sendFullHeightLazyLoadData, true);
8✔
1060
        }
1061
    }
1062

1063
    private unregisterLazyLoadEvents() {
1064
        if (this.viewConfig.fullHeight && this.viewConfig.lazyLoadingForFullHeight) {
10✔
1065
            window.removeEventListener('resize', this.sendFullHeightLazyLoadData);
1✔
1066
            window.removeEventListener('scroll', this.sendFullHeightLazyLoadData);
1✔
1067
        }
1068
    }
1069

1070
    /**
1071
     * Renders the embedded application pages in the ThoughtSpot app.
1072
     * @param renderOptions An object containing the page ID
1073
     * to be embedded.
1074
     */
1075
    public async render(): Promise<AppEmbed> {
1076
        await super.render();
140✔
1077

1078
        const src = this.getIFrameSrc();
140✔
1079
        await this.renderV1Embed(src);
140✔
1080

1081
        this.postRender();
140✔
1082
        return this;
140✔
1083
    }
1084
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc