@thoughtspot/visual-embed-sdk 1.37.0 → 1.37.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.
Files changed (95) hide show
  1. package/cjs/package.json +2 -2
  2. package/cjs/src/auth.d.ts +9 -4
  3. package/cjs/src/auth.d.ts.map +1 -1
  4. package/cjs/src/auth.js +21 -0
  5. package/cjs/src/auth.js.map +1 -1
  6. package/cjs/src/auth.spec.js +48 -0
  7. package/cjs/src/auth.spec.js.map +1 -1
  8. package/cjs/src/embed/app.d.ts +99 -0
  9. package/cjs/src/embed/app.d.ts.map +1 -1
  10. package/cjs/src/embed/app.js +48 -3
  11. package/cjs/src/embed/app.js.map +1 -1
  12. package/cjs/src/embed/app.spec.js +61 -0
  13. package/cjs/src/embed/app.spec.js.map +1 -1
  14. package/cjs/src/embed/base.d.ts.map +1 -1
  15. package/cjs/src/embed/base.js +7 -0
  16. package/cjs/src/embed/base.js.map +1 -1
  17. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  18. package/cjs/src/embed/ts-embed.js +10 -5
  19. package/cjs/src/embed/ts-embed.js.map +1 -1
  20. package/cjs/src/embed/ts-embed.spec.d.ts.map +1 -1
  21. package/cjs/src/embed/ts-embed.spec.js +105 -0
  22. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  23. package/cjs/src/types.d.ts +69 -4
  24. package/cjs/src/types.d.ts.map +1 -1
  25. package/cjs/src/types.js +54 -2
  26. package/cjs/src/types.js.map +1 -1
  27. package/cjs/src/utils/graphql/answerService/answerService.d.ts +35 -15
  28. package/cjs/src/utils/graphql/answerService/answerService.d.ts.map +1 -1
  29. package/cjs/src/utils/graphql/answerService/answerService.js +35 -15
  30. package/cjs/src/utils/graphql/answerService/answerService.js.map +1 -1
  31. package/cjs/src/utils/processData.js +6 -4
  32. package/cjs/src/utils/processData.js.map +1 -1
  33. package/dist/{index-DnIvX1FR.js → index-m9UtENc9.js} +1 -1
  34. package/dist/src/auth.d.ts +9 -4
  35. package/dist/src/auth.d.ts.map +1 -1
  36. package/dist/src/embed/app.d.ts +99 -0
  37. package/dist/src/embed/app.d.ts.map +1 -1
  38. package/dist/src/embed/base.d.ts.map +1 -1
  39. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  40. package/dist/src/embed/ts-embed.spec.d.ts.map +1 -1
  41. package/dist/src/types.d.ts +69 -4
  42. package/dist/src/types.d.ts.map +1 -1
  43. package/dist/src/utils/graphql/answerService/answerService.d.ts +35 -15
  44. package/dist/src/utils/graphql/answerService/answerService.d.ts.map +1 -1
  45. package/dist/tsembed-react.es.js +183 -31
  46. package/dist/tsembed-react.js +182 -30
  47. package/dist/tsembed.es.js +183 -31
  48. package/dist/tsembed.js +182 -30
  49. package/dist/visual-embed-sdk-react-full.d.ts +212 -23
  50. package/dist/visual-embed-sdk-react.d.ts +212 -23
  51. package/dist/visual-embed-sdk.d.ts +212 -23
  52. package/lib/package.json +2 -2
  53. package/lib/src/auth.d.ts +9 -4
  54. package/lib/src/auth.d.ts.map +1 -1
  55. package/lib/src/auth.js +21 -0
  56. package/lib/src/auth.js.map +1 -1
  57. package/lib/src/auth.spec.js +48 -0
  58. package/lib/src/auth.spec.js.map +1 -1
  59. package/lib/src/embed/app.d.ts +99 -0
  60. package/lib/src/embed/app.d.ts.map +1 -1
  61. package/lib/src/embed/app.js +47 -2
  62. package/lib/src/embed/app.js.map +1 -1
  63. package/lib/src/embed/app.spec.js +62 -1
  64. package/lib/src/embed/app.spec.js.map +1 -1
  65. package/lib/src/embed/base.d.ts.map +1 -1
  66. package/lib/src/embed/base.js +7 -0
  67. package/lib/src/embed/base.js.map +1 -1
  68. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  69. package/lib/src/embed/ts-embed.js +10 -5
  70. package/lib/src/embed/ts-embed.js.map +1 -1
  71. package/lib/src/embed/ts-embed.spec.d.ts.map +1 -1
  72. package/lib/src/embed/ts-embed.spec.js +105 -0
  73. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  74. package/lib/src/types.d.ts +69 -4
  75. package/lib/src/types.d.ts.map +1 -1
  76. package/lib/src/types.js +54 -2
  77. package/lib/src/types.js.map +1 -1
  78. package/lib/src/utils/graphql/answerService/answerService.d.ts +35 -15
  79. package/lib/src/utils/graphql/answerService/answerService.d.ts.map +1 -1
  80. package/lib/src/utils/graphql/answerService/answerService.js +35 -15
  81. package/lib/src/utils/graphql/answerService/answerService.js.map +1 -1
  82. package/lib/src/utils/processData.js +6 -4
  83. package/lib/src/utils/processData.js.map +1 -1
  84. package/lib/src/visual-embed-sdk.d.ts +212 -23
  85. package/package.json +2 -2
  86. package/src/auth.spec.ts +52 -0
  87. package/src/auth.ts +25 -3
  88. package/src/embed/app.spec.ts +88 -0
  89. package/src/embed/app.ts +133 -0
  90. package/src/embed/base.ts +9 -0
  91. package/src/embed/ts-embed.spec.ts +130 -0
  92. package/src/embed/ts-embed.ts +10 -4
  93. package/src/types.ts +68 -3
  94. package/src/utils/graphql/answerService/answerService.ts +35 -15
  95. package/src/utils/processData.ts +6 -6
@@ -106,6 +106,43 @@ declare module '@thoughtspot/visual-embed-sdk/embed/app' {
106
106
  AI_ANSWER = "aiAnswer",
107
107
  NONE = "none"
108
108
  }
109
+ /**
110
+ * Define the version of the primary navbar
111
+ * @version SDK: 1.39.0 | Thoughtspot: 10.10.0.cl
112
+ */
113
+ export enum PrimaryNavbarVersion {
114
+ /**
115
+ * Sliding (v3) introduces a new left-side navigation hub featuring a tab switcher,
116
+ * along with updates to the top navigation bar.
117
+ * It serves as the foundational version of the PrimaryNavBar.
118
+ */
119
+ Sliding = "v3"
120
+ }
121
+ /**
122
+ * Define the version of the home page
123
+ * @version SDK: 1.39.0 | Thoughtspot: 10.10.0.cl
124
+ */
125
+ export enum HomePage {
126
+ /**
127
+ * Modular (v2) introduces the updated Modular Home Experience.
128
+ * It serves as the foundational version of the home page.
129
+ */
130
+ Modular = "v2"
131
+ }
132
+ /**
133
+ * Define the discovery experience
134
+ * @version SDK: 1.39.0 | Thoughtspot: 10.10.0.cl
135
+ */
136
+ export interface DiscoveryExperience {
137
+ /**
138
+ * primaryNavbarVersion determines the version of the navigation version.
139
+ */
140
+ primaryNavbarVersion?: PrimaryNavbarVersion;
141
+ /**
142
+ * homePage determines the version of the home page.
143
+ */
144
+ homePage?: HomePage;
145
+ }
109
146
  /**
110
147
  * The view configuration for full app embedding.
111
148
  * @group Embed components
@@ -174,6 +211,51 @@ declare module '@thoughtspot/visual-embed-sdk/embed/app' {
174
211
  * ```
175
212
  */
176
213
  enablePendoHelp?: boolean;
214
+ /**
215
+ * Control the visibility of the hamburger icon on the top nav bar
216
+ * available when new navigation V3 is enabled.
217
+ *
218
+ * @default false
219
+ * @version SDK: 1.39.0 | Thoughtspot: 10.10.0.cl
220
+ * @example
221
+ * ```js
222
+ * const embed = new AppEmbed('#tsEmbed', {
223
+ * ... // other options
224
+ * hideHamburger : true,
225
+ * })
226
+ * ```
227
+ */
228
+ hideHamburger?: boolean;
229
+ /**
230
+ * Control the visibility of the Eureka search on the top nav bar
231
+ * this will control for both new V2 and new navigation V3.
232
+ *
233
+ * @default true
234
+ * @version SDK: 1.39.0 | Thoughtspot: 10.10.0.cl
235
+ * @example
236
+ * ```js
237
+ * const embed = new AppEmbed('#tsEmbed', {
238
+ * ... // other options
239
+ * hideObjectSearch: false,
240
+ * })
241
+ * ```
242
+ */
243
+ hideObjectSearch?: boolean;
244
+ /**
245
+ * Control the visibility of the notification on the top nav bar V3,
246
+ * available when new navigation V3 is enabled.
247
+ *
248
+ * @default true
249
+ * @version SDK: 1.39.0 | Thoughtspot: 10.10.0.cl
250
+ * @example
251
+ * ```js
252
+ * const embed = new AppEmbed('#tsEmbed', {
253
+ * ... // other options
254
+ * hideNotification: false,
255
+ * })
256
+ * ```
257
+ */
258
+ hideNotification?: boolean;
177
259
  /**
178
260
  * Control the visibility of the application switcher button on the nav-bar.
179
261
  * By default, the application switcher is shown.
@@ -367,6 +449,23 @@ declare module '@thoughtspot/visual-embed-sdk/embed/app' {
367
449
  * ```
368
450
  */
369
451
  modularHomeExperience?: boolean;
452
+ /**
453
+ * To configure the top-left navigation and home page experience
454
+ *
455
+ * @default false
456
+ * @version SDK: 1.39.0 | Thoughtspot: 10.10.0.cl
457
+ * @example
458
+ * ```js
459
+ * const embed = new AppEmbed('#tsEmbed', {
460
+ * ... // other options
461
+ * discoveryExperience : {
462
+ * primaryNavbarVersion: PrimaryNavbarVersion.Sliding,
463
+ * homePage: HompePage.Modular,
464
+ * },
465
+ * })
466
+ * ```
467
+ */
468
+ discoveryExperience?: DiscoveryExperience;
370
469
  /**
371
470
  * Boolean to control if Liveboard header is sticky or not.
372
471
  * @example
@@ -1735,7 +1834,8 @@ declare module '@thoughtspot/visual-embed-sdk/auth' {
1735
1834
  NO_COOKIE_ACCESS = "NO_COOKIE_ACCESS",
1736
1835
  EXPIRY = "EXPIRY",
1737
1836
  OTHER = "OTHER",
1738
- IDLE_SESSION_TIMEOUT = "IDLE_SESSION_TIMEOUT"
1837
+ IDLE_SESSION_TIMEOUT = "IDLE_SESSION_TIMEOUT",
1838
+ UNAUTHENTICATED_FAILURE = "UNAUTHENTICATED_FAILURE"
1739
1839
  }
1740
1840
  /**
1741
1841
  * Enum for auth status emitted by the emitter returned from {@link init}.
@@ -1769,7 +1869,11 @@ declare module '@thoughtspot/visual-embed-sdk/auth' {
1769
1869
  * or by the trigger button.
1770
1870
  * @version SDK: 1.19.0
1771
1871
  */
1772
- WAITING_FOR_POPUP = "WAITING_FOR_POPUP"
1872
+ WAITING_FOR_POPUP = "WAITING_FOR_POPUP",
1873
+ /**
1874
+ * Emitted when the SAML popup is closed without authentication
1875
+ */
1876
+ SAML_POPUP_CLOSED_NO_AUTH = "SAML_POPUP_CLOSED_NO_AUTH"
1773
1877
  }
1774
1878
  /**
1775
1879
  * Event emitter returned from {@link init}.
@@ -1787,10 +1891,10 @@ declare module '@thoughtspot/visual-embed-sdk/auth' {
1787
1891
  * @param event
1788
1892
  * @param listener
1789
1893
  */
1790
- on(event: AuthStatus.SDK_SUCCESS | AuthStatus.LOGOUT | AuthStatus.WAITING_FOR_POPUP, listener: () => void): this;
1894
+ on(event: AuthStatus.SDK_SUCCESS | AuthStatus.LOGOUT | AuthStatus.WAITING_FOR_POPUP | AuthStatus.SAML_POPUP_CLOSED_NO_AUTH, listener: () => void): this;
1791
1895
  on(event: AuthStatus.SUCCESS, listener: (sessionInfo: any) => void): this;
1792
1896
  once(event: AuthStatus.FAILURE, listener: (failureType: AuthFailureType) => void): this;
1793
- once(event: AuthStatus.SDK_SUCCESS | AuthStatus.LOGOUT | AuthStatus.WAITING_FOR_POPUP, listener: () => void): this;
1897
+ once(event: AuthStatus.SDK_SUCCESS | AuthStatus.LOGOUT | AuthStatus.WAITING_FOR_POPUP | AuthStatus.SAML_POPUP_CLOSED_NO_AUTH, listener: () => void): this;
1794
1898
  once(event: AuthStatus.SUCCESS, listener: (sessionInfo: any) => void): this;
1795
1899
  /**
1796
1900
  * Trigger an event on the emitter returned from init.
@@ -2206,7 +2310,19 @@ declare module '@thoughtspot/visual-embed-sdk/types' {
2206
2310
  /**
2207
2311
  * @version SDK: 1.34.0| ThoughtSpot: 10.3.0.cl
2208
2312
  */
2209
- LiveboardSchedules = "liveboard-schedules"
2313
+ LiveboardSchedules = "liveboard-schedules",
2314
+ /**
2315
+ * Create new options in the insights left navigation,
2316
+ * available when new navigation V3 is enabled.
2317
+ * @version SDK: 1.39.0 | ThoughtSpot: 10.10.0.cl
2318
+ */
2319
+ Create = "create",
2320
+ /**
2321
+ * Spotter option in the insights left navigation,
2322
+ * available when new navigation V3 is enabled.
2323
+ * @version SDK: 1.39.0 | ThoughtSpot: 10.10.0.cl
2324
+ */
2325
+ Spotter = "spotter"
2210
2326
  }
2211
2327
  export type DOMSelector = string | HTMLElement;
2212
2328
  /**
@@ -2687,6 +2803,19 @@ declare module '@thoughtspot/visual-embed-sdk/types' {
2687
2803
  * ```
2688
2804
  */
2689
2805
  disabledActions?: Action[];
2806
+ /**
2807
+ * The primary action to display on top of the viz for Liveboard and App Embed.
2808
+ * Use this to set the primary action.
2809
+ * @version SDK: 1.37.0 | ThoughtSpot: 10.9.0.cl
2810
+ * @example
2811
+ * ```js
2812
+ * const embed = new LiveboardEmbed('#embed', {
2813
+ * ... // other liveboard view config
2814
+ * primaryAction: Action.Download
2815
+ * });
2816
+ * ```
2817
+ */
2818
+ primaryAction?: Action | string;
2690
2819
  /**
2691
2820
  * The tooltip to display for disabled actions.
2692
2821
  * @version SDK: 1.6.0 | ThoughtSpot: ts8.nov.cl, 8.4.1.sw
@@ -4606,9 +4735,12 @@ declare module '@thoughtspot/visual-embed-sdk/types' {
4606
4735
  * @example
4607
4736
  * ```js
4608
4737
  * searchEmbed.trigger(HostEvent.OpenFilter,
4609
- * { columnId: '<column-GUID>', name: 'column name', type: 'INT64', dataType: 'ATTRIBUTE'})
4738
+ * {column: { columnId: '<column-GUID>', name: 'column name', type: 'INT64', dataType: 'ATTRIBUTE'}})
4739
+ * ```
4740
+ * @example
4741
+ * ```js
4610
4742
  * LiveboardEmbed.trigger(HostEvent.OpenFilter,
4611
- * { columnId: '<column-GUID>'})
4743
+ * { column: {columnId: '<column-GUID>'}})
4612
4744
  * ```
4613
4745
  * @version SDK: 1.21.0 | ThoughtSpot: 9.2.0.cl
4614
4746
  */
@@ -5217,6 +5349,25 @@ declare module '@thoughtspot/visual-embed-sdk/types' {
5217
5349
  * }]
5218
5350
  * });
5219
5351
  * ```
5352
+ * If there are multiple columns with the same name, consider
5353
+ * using `WORKSHEET_NAME::COLUMN_NAME` format.
5354
+ *
5355
+ * @example
5356
+ *
5357
+ * ```js
5358
+ * liveboardEmbed.trigger(HostEvent.UpdateFilters, {
5359
+ * filters: [{
5360
+ * column: "(Sample) Retail - Apparel::city",
5361
+ * oper: 'IN',
5362
+ * values: ["atlanta"]
5363
+ * },
5364
+ * {
5365
+ * column: "(Sample) Retail - Apparel::Region",
5366
+ * oper: 'IN',
5367
+ * values: ["West","Midwest"]
5368
+ * }]
5369
+ * });
5370
+ * ```
5220
5371
  * @version SDK: 1.23.0 | ThoughtSpot: 9.4.0.cl
5221
5372
  */
5222
5373
  UpdateFilters = "updateFilters",
@@ -5276,8 +5427,17 @@ declare module '@thoughtspot/visual-embed-sdk/types' {
5276
5427
  /**
5277
5428
  * Get the Answer session for a Search or
5278
5429
  * Liveboard visualization.
5430
+ *
5431
+ * Note: This event is not typically used directly. Instead, use the
5432
+ * `getAnswerService()` method on the embed instance to get an AnswerService
5433
+ * object that provides a more convenient interface for working with answers.
5434
+ *
5279
5435
  * @example
5280
5436
  * ```js
5437
+ * // Preferred way to get an AnswerService
5438
+ * const service = await embed.getAnswerService();
5439
+ *
5440
+ * // Alternative direct usage (not recommended)
5281
5441
  * const {session} = await embed.trigger(
5282
5442
  * HostEvent.GetAnswerSession, {
5283
5443
  * vizId: '123', // For Liveboard Visualization.
@@ -5285,6 +5445,10 @@ declare module '@thoughtspot/visual-embed-sdk/types' {
5285
5445
  * ```
5286
5446
  * @example
5287
5447
  * ```js
5448
+ * // Preferred way to get an AnswerService
5449
+ * const service = await embed.getAnswerService();
5450
+ *
5451
+ * // Alternative direct usage (not recommended)
5288
5452
  * const {session} = await embed.trigger( HostEvent.GetAnswerSession )
5289
5453
  * ```
5290
5454
  * @version SDK: 1.26.0 | ThoughtSpot: 9.10.0.cl, 10.1.0.sw
@@ -5483,6 +5647,10 @@ declare module '@thoughtspot/visual-embed-sdk/types' {
5483
5647
  ShowInsertToSlide = "insertInToSlide",
5484
5648
  PrimaryNavHidden = "primaryNavHidden",
5485
5649
  HideProfleAndHelp = "profileAndHelpInNavBarHidden",
5650
+ NavigationVersion = "navigationVersion",
5651
+ HideHamburger = "hideHamburger",
5652
+ HideObjectSearch = "hideObjectSearch",
5653
+ HideNotification = "hideNotification",
5486
5654
  HideApplicationSwitcher = "applicationSwitcherHidden",
5487
5655
  HideOrgSwitcher = "orgSwitcherHidden",
5488
5656
  IsSageEmbed = "isSageEmbed",
@@ -5538,7 +5706,8 @@ declare module '@thoughtspot/visual-embed-sdk/types' {
5538
5706
  DataSourceId = "dataSourceId",
5539
5707
  preAuthCache = "preAuthCache",
5540
5708
  ShowSpotterLimitations = "showSpotterLimitations",
5541
- CoverAndFilterOptionInPDF = "coverAndFilterOptionInPDF"
5709
+ CoverAndFilterOptionInPDF = "coverAndFilterOptionInPDF",
5710
+ PrimaryAction = "primaryAction"
5542
5711
  }
5543
5712
  /**
5544
5713
  * ThoughtSpot application pages include actions and menu commands
@@ -7478,29 +7647,49 @@ declare module '@thoughtspot/visual-embed-sdk/utils/graphql/answerService/answer
7478
7647
  dataValue: any;
7479
7648
  }
7480
7649
  /**
7481
- * Class representing the answer service provided with the
7482
- * custom action payload. This service could be used to run
7483
- * graphql queries in the context of the answer on which the
7484
- * custom action was triggered.
7650
+ * AnswerService provides a simple way to work with ThoughtSpot Answers.
7651
+ *
7652
+ * This service allows you to interact with ThoughtSpot Answers programmatically,
7653
+ * making it easy to customize visualizations, filter data, and extract insights
7654
+ * directly from your application.
7655
+ *
7656
+ * You can use this service to:
7657
+ * - Add or remove columns from Answers (`addColumns`, `removeColumns`, `addColumnsByName`)
7658
+ * - Apply filters to Answers (`addFilter`)
7659
+ * - Get data from Answers in different formats (JSON, CSV, PNG) (`fetchData`, `fetchCSVBlob`, `fetchPNGBlob`)
7660
+ * - Get data for specific points in visualizations (`getUnderlyingDataForPoint`)
7661
+ * - Run custom queries (`executeQuery`)
7662
+ * - Add visualizations to liveboards (`addDisplayedVizToLiveboard`)
7663
+ *
7485
7664
  * @example
7486
7665
  * ```js
7487
- * embed.on(EmbedEvent.CustomAction, e => {
7488
- * const underlying = await e.answerService.getUnderlyingDataForPoint([
7489
- * 'col name 1'
7490
- * ]);
7491
- * const data = await underlying.fetchData(0, 100);
7492
- * })
7666
+ * // Get the answer service
7667
+ * embed.on(EmbedEvent.Data, async (e) => {
7668
+ * const service = await embed.getAnswerService();
7669
+ *
7670
+ * // Add columns to the answer
7671
+ * await service.addColumnsByName(["Sales", "Region"]);
7672
+ *
7673
+ * // Get the data
7674
+ * const data = await service.fetchData();
7675
+ * console.log(data);
7676
+ * });
7493
7677
  * ```
7678
+ *
7494
7679
  * @example
7495
7680
  * ```js
7496
- * embed.on(EmbedEvent.Data, async (e) => {
7497
- * const service = await embed.getAnswerService();
7498
- * await service.addColumns([
7499
- * "<column guid>"
7681
+ * // Get data for a point in a visualization
7682
+ * embed.on(EmbedEvent.CustomAction, async (e) => {
7683
+ * const underlying = await e.answerService.getUnderlyingDataForPoint([
7684
+ * 'Product Name',
7685
+ * 'Sales Amount'
7500
7686
  * ]);
7501
- * console.log(await service.fetchData());
7687
+ *
7688
+ * const data = await underlying.fetchData(0, 100);
7689
+ * console.log(data);
7502
7690
  * });
7503
7691
  * ```
7692
+ *
7504
7693
  * @version SDK: 1.25.0| ThoughtSpot: 9.10.0.cl
7505
7694
  * @group Events
7506
7695
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thoughtspot/visual-embed-sdk",
3
- "version": "1.37.0",
3
+ "version": "1.37.1",
4
4
  "description": "ThoughtSpot Embed SDK",
5
5
  "module": "lib/src/index.js",
6
6
  "main": "dist/tsembed.js",
@@ -141,7 +141,7 @@
141
141
  "typescript-eslint": "^8.29.1",
142
142
  "url-search-params-polyfill": "^8.1.0",
143
143
  "util": "^0.12.4",
144
- "vite": "^5.3.4"
144
+ "vite": "^6.3.5"
145
145
  },
146
146
  "author": "ThoughtSpot",
147
147
  "email": "support@thoughtspot.com",
package/src/auth.spec.ts CHANGED
@@ -385,6 +385,45 @@ describe('Unit test for auth', () => {
385
385
  expect(global.window.location.href).toBe(samalLoginUrl);
386
386
  });
387
387
 
388
+ it('should emit SAML_POPUP_CLOSED_NO_AUTH when popup window is closed', async () => {
389
+ jest.useFakeTimers();
390
+ const mockPopupWindow = {
391
+ closed: false,
392
+ focus: jest.fn(),
393
+ close: jest.fn()
394
+ };
395
+ global.window.open = jest.fn().mockReturnValue(mockPopupWindow);
396
+ Object.defineProperty(window, 'location', {
397
+ value: {
398
+ href: '',
399
+ hash: '',
400
+ },
401
+ });
402
+ spyOn(authInstance, 'samlCompletionPromise').and.returnValue(Promise.resolve(false));
403
+ const emitSpy = jest.fn();
404
+ const mockEventEmitter = {
405
+ emit: emitSpy,
406
+ once: jest.fn(),
407
+ on: jest.fn()
408
+ };
409
+ authInstance.setAuthEE(mockEventEmitter as any);
410
+ jest.spyOn(tokenAuthService, 'isActiveService')
411
+ .mockReturnValueOnce(false)
412
+ .mockReturnValueOnce(true);
413
+ expect(
414
+ await authInstance.doSamlAuth({
415
+ ...embedConfig.doSamlAuthNoRedirect,
416
+ }),
417
+ ).toBe(true);
418
+ document.getElementById('ts-auth-btn').click();
419
+ mockPopupWindow.closed = true;
420
+ jest.advanceTimersByTime(1000);
421
+ window.postMessage({ type: EmbedEvent.SAMLComplete }, '*');
422
+ await authInstance.samlCompletionPromise;
423
+ expect(emitSpy).toHaveBeenCalledWith(authInstance.AuthStatus.SAML_POPUP_CLOSED_NO_AUTH);
424
+ jest.useRealTimers();
425
+ authInstance.setAuthEE(null);
426
+ });
388
427
  it('when user is not loggedIn, in config noRedirect is true and isAtSSORedirectUrl is false', async () => {
389
428
  Object.defineProperty(window, 'location', {
390
429
  value: {
@@ -408,6 +447,19 @@ describe('Unit test for auth', () => {
408
447
  await authInstance.samlCompletionPromise;
409
448
  expect(authInstance.loggedInStatus).toBe(true);
410
449
  });
450
+
451
+ it('should support emitting SAML_POPUP_CLOSED_NO_AUTH event', () => {
452
+ const emitSpy = jest.fn();
453
+ const mockEventEmitter = {
454
+ emit: emitSpy,
455
+ once: jest.fn()
456
+ };
457
+ authInstance.setAuthEE(mockEventEmitter as any);
458
+ authInstance.getAuthEE().emit(authInstance.AuthStatus.SAML_POPUP_CLOSED_NO_AUTH);
459
+ expect(emitSpy).toHaveBeenCalledWith(authInstance.AuthStatus.SAML_POPUP_CLOSED_NO_AUTH);
460
+ authInstance.setAuthEE(null);
461
+ });
462
+
411
463
  });
412
464
 
413
465
  describe('doOIDCAuth', () => {
package/src/auth.ts CHANGED
@@ -40,7 +40,8 @@ export enum AuthFailureType {
40
40
  NO_COOKIE_ACCESS = 'NO_COOKIE_ACCESS',
41
41
  EXPIRY = 'EXPIRY',
42
42
  OTHER = 'OTHER',
43
- IDLE_SESSION_TIMEOUT = 'IDLE_SESSION_TIMEOUT'
43
+ IDLE_SESSION_TIMEOUT = 'IDLE_SESSION_TIMEOUT',
44
+ UNAUTHENTICATED_FAILURE = 'UNAUTHENTICATED_FAILURE',
44
45
  }
45
46
 
46
47
  /**
@@ -76,6 +77,11 @@ export enum AuthStatus {
76
77
  * @version SDK: 1.19.0
77
78
  */
78
79
  WAITING_FOR_POPUP = 'WAITING_FOR_POPUP',
80
+
81
+ /**
82
+ * Emitted when the SAML popup is closed without authentication
83
+ */
84
+ SAML_POPUP_CLOSED_NO_AUTH = 'SAML_POPUP_CLOSED_NO_AUTH',
79
85
  }
80
86
 
81
87
  /**
@@ -95,13 +101,13 @@ export interface AuthEventEmitter {
95
101
  * @param listener
96
102
  */
97
103
  on(
98
- event: AuthStatus.SDK_SUCCESS | AuthStatus.LOGOUT | AuthStatus.WAITING_FOR_POPUP,
104
+ event: AuthStatus.SDK_SUCCESS | AuthStatus.LOGOUT | AuthStatus.WAITING_FOR_POPUP | AuthStatus.SAML_POPUP_CLOSED_NO_AUTH,
99
105
  listener: () => void,
100
106
  ): this;
101
107
  on(event: AuthStatus.SUCCESS, listener: (sessionInfo: any) => void): this;
102
108
  once(event: AuthStatus.FAILURE, listener: (failureType: AuthFailureType) => void): this;
103
109
  once(
104
- event: AuthStatus.SDK_SUCCESS | AuthStatus.LOGOUT | AuthStatus.WAITING_FOR_POPUP,
110
+ event: AuthStatus.SDK_SUCCESS | AuthStatus.LOGOUT | AuthStatus.WAITING_FOR_POPUP | AuthStatus.SAML_POPUP_CLOSED_NO_AUTH,
105
111
  listener: () => void,
106
112
  ): this;
107
113
  once(event: AuthStatus.SUCCESS, listener: (sessionInfo: any) => void): this;
@@ -363,6 +369,7 @@ export const doBasicAuth = async (embedConfig: EmbedConfig): Promise<boolean> =>
363
369
  * @param triggerText
364
370
  */
365
371
  async function samlPopupFlow(ssoURL: string, triggerContainer: DOMSelector, triggerText: string) {
372
+ let popupClosedCheck: NodeJS.Timeout;
366
373
  const openPopup = () => {
367
374
  if (samlAuthWindow === null || samlAuthWindow.closed) {
368
375
  samlAuthWindow = window.open(
@@ -370,10 +377,21 @@ async function samlPopupFlow(ssoURL: string, triggerContainer: DOMSelector, trig
370
377
  '_blank',
371
378
  'location=no,height=570,width=520,scrollbars=yes,status=yes',
372
379
  );
380
+ if (samlAuthWindow) {
381
+ popupClosedCheck = setInterval(() => {
382
+ if (samlAuthWindow.closed) {
383
+ clearInterval(popupClosedCheck);
384
+ if (samlCompletionPromise && !samlCompletionResolved) {
385
+ authEE?.emit(AuthStatus.SAML_POPUP_CLOSED_NO_AUTH);
386
+ }
387
+ }
388
+ }, 500);
389
+ }
373
390
  } else {
374
391
  samlAuthWindow.focus();
375
392
  }
376
393
  };
394
+ let samlCompletionResolved = false;
377
395
  authEE?.emit(AuthStatus.WAITING_FOR_POPUP);
378
396
  const containerEl = getDOMNode(triggerContainer);
379
397
  if (containerEl) {
@@ -385,6 +403,10 @@ async function samlPopupFlow(ssoURL: string, triggerContainer: DOMSelector, trig
385
403
  samlCompletionPromise = samlCompletionPromise || new Promise<void>((resolve, reject) => {
386
404
  window.addEventListener('message', (e) => {
387
405
  if (e.data.type === EmbedEvent.SAMLComplete) {
406
+ samlCompletionResolved = true;
407
+ if (popupClosedCheck) {
408
+ clearInterval(popupClosedCheck);
409
+ }
388
410
  (e.source as Window).close();
389
411
  resolve();
390
412
  }
@@ -4,6 +4,8 @@ import {
4
4
  DataPanelCustomColumnGroupsAccordionState,
5
5
  Page,
6
6
  HomePageSearchBarMode,
7
+ PrimaryNavbarVersion,
8
+ HomePage,
7
9
  } from './app';
8
10
  import { init } from '../index';
9
11
  import {
@@ -494,6 +496,92 @@ describe('App embed tests', () => {
494
496
  });
495
497
  });
496
498
 
499
+ test('Should add hideHamburger, hideObjectSearch, hideNotification flags to the iframe src', async () => {
500
+ const appEmbed = new AppEmbed(getRootEl(), {
501
+ ...defaultViewConfig,
502
+ hideHamburger: true,
503
+ hideObjectSearch: true,
504
+ hideNotification: true,
505
+ } as AppViewConfig);
506
+
507
+ appEmbed.render();
508
+ await executeAfterWait(() => {
509
+ expectUrlMatchesWithParams(
510
+ getIFrameSrc(),
511
+ `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&modularHomeExperience=false&hideHamburger=true&hideObjectSearch=true&hideNotification=true${defaultParams}${defaultParamsPost}#/home`,
512
+ );
513
+ });
514
+ });
515
+
516
+ test('Should not add when hideHamburger, hideObjectSearch, hideNotification values are not true to the iframe src', async () => {
517
+ const appEmbed = new AppEmbed(getRootEl(), {
518
+ ...defaultViewConfig,
519
+ hideHamburger: false,
520
+ hideObjectSearch: undefined,
521
+ hideNotification: null,
522
+ } as AppViewConfig);
523
+
524
+ appEmbed.render();
525
+ await executeAfterWait(() => {
526
+ expectUrlMatchesWithParams(
527
+ getIFrameSrc(),
528
+ `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&modularHomeExperience=false${defaultParams}${defaultParamsPost}#/home`,
529
+ );
530
+ });
531
+ });
532
+
533
+ test('Should add navigationVersion=v3 when primaryNavbarVersion is Sliding to the iframe src', async () => {
534
+ const appEmbed = new AppEmbed(getRootEl(), {
535
+ ...defaultViewConfig,
536
+ discoveryExperience: {
537
+ primaryNavbarVersion: PrimaryNavbarVersion.Sliding,
538
+ homePage: HomePage.Modular,
539
+ },
540
+ } as AppViewConfig);
541
+
542
+ appEmbed.render();
543
+ await executeAfterWait(() => {
544
+ expectUrlMatchesWithParams(
545
+ getIFrameSrc(),
546
+ `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&modularHomeExperience=true&navigationVersion=v3${defaultParams}${defaultParamsPost}#/home`,
547
+ );
548
+ });
549
+ });
550
+
551
+ test('Should not add navigationVersion=v3 when primaryNavbarVersion is not added to the iframe src', async () => {
552
+ const appEmbed = new AppEmbed(getRootEl(), {
553
+ ...defaultViewConfig,
554
+ discoveryExperience: {
555
+ homePage: HomePage.Modular,
556
+ },
557
+ } as AppViewConfig);
558
+
559
+ appEmbed.render();
560
+ await executeAfterWait(() => {
561
+ expectUrlMatchesWithParams(
562
+ getIFrameSrc(),
563
+ `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&modularHomeExperience=true${defaultParams}${defaultParamsPost}#/home`,
564
+ );
565
+ });
566
+ });
567
+
568
+ test('Should add navigationVersion=v3 & modularHomeExperience=false when primaryNavbarVersion is Sliding to the iframe src', async () => {
569
+ const appEmbed = new AppEmbed(getRootEl(), {
570
+ ...defaultViewConfig,
571
+ discoveryExperience: {
572
+ primaryNavbarVersion: PrimaryNavbarVersion.Sliding,
573
+ },
574
+ } as AppViewConfig);
575
+
576
+ appEmbed.render();
577
+ await executeAfterWait(() => {
578
+ expectUrlMatchesWithParams(
579
+ getIFrameSrc(),
580
+ `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&modularHomeExperience=false&navigationVersion=v3${defaultParams}${defaultParamsPost}#/home`,
581
+ );
582
+ });
583
+ });
584
+
497
585
  test('Should add enableAskSage flag to the iframe src', async () => {
498
586
  const appEmbed = new AppEmbed(getRootEl(), {
499
587
  ...defaultViewConfig,