@thoughtspot/visual-embed-sdk 1.47.2 → 1.47.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 (144) hide show
  1. package/cjs/package.json +1 -1
  2. package/cjs/src/embed/app.d.ts +6 -1
  3. package/cjs/src/embed/app.d.ts.map +1 -1
  4. package/cjs/src/embed/app.js +4 -1
  5. package/cjs/src/embed/app.js.map +1 -1
  6. package/cjs/src/embed/app.spec.js +112 -0
  7. package/cjs/src/embed/app.spec.js.map +1 -1
  8. package/cjs/src/embed/conversation.d.ts +2 -1
  9. package/cjs/src/embed/conversation.d.ts.map +1 -1
  10. package/cjs/src/embed/conversation.js.map +1 -1
  11. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  12. package/cjs/src/embed/liveboard.js +4 -1
  13. package/cjs/src/embed/liveboard.js.map +1 -1
  14. package/cjs/src/embed/liveboard.spec.js +22 -0
  15. package/cjs/src/embed/liveboard.spec.js.map +1 -1
  16. package/cjs/src/embed/search.d.ts +24 -1
  17. package/cjs/src/embed/search.d.ts.map +1 -1
  18. package/cjs/src/embed/search.js +15 -2
  19. package/cjs/src/embed/search.js.map +1 -1
  20. package/cjs/src/embed/search.spec.js +99 -0
  21. package/cjs/src/embed/search.spec.js.map +1 -1
  22. package/cjs/src/embed/spotter-utils.d.ts +3 -0
  23. package/cjs/src/embed/spotter-utils.d.ts.map +1 -1
  24. package/cjs/src/embed/spotter-utils.js +11 -3
  25. package/cjs/src/embed/spotter-utils.js.map +1 -1
  26. package/cjs/src/embed/spotter-utils.spec.js +51 -0
  27. package/cjs/src/embed/spotter-utils.spec.js.map +1 -1
  28. package/cjs/src/embed/ts-embed.d.ts +1 -0
  29. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  30. package/cjs/src/embed/ts-embed.js +15 -3
  31. package/cjs/src/embed/ts-embed.js.map +1 -1
  32. package/cjs/src/embed/ts-embed.spec.js +139 -0
  33. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  34. package/cjs/src/index.d.ts +2 -2
  35. package/cjs/src/index.d.ts.map +1 -1
  36. package/cjs/src/index.js +8 -1
  37. package/cjs/src/index.js.map +1 -1
  38. package/cjs/src/mixpanel-service.d.ts.map +1 -1
  39. package/cjs/src/mixpanel-service.js +2 -0
  40. package/cjs/src/mixpanel-service.js.map +1 -1
  41. package/cjs/src/mixpanel-service.spec.js +2 -0
  42. package/cjs/src/mixpanel-service.spec.js.map +1 -1
  43. package/cjs/src/test/test-utils.d.ts +1 -0
  44. package/cjs/src/test/test-utils.d.ts.map +1 -1
  45. package/cjs/src/test/test-utils.js +26 -1
  46. package/cjs/src/test/test-utils.js.map +1 -1
  47. package/cjs/src/types.d.ts +424 -0
  48. package/cjs/src/types.d.ts.map +1 -1
  49. package/cjs/src/types.js +121 -1
  50. package/cjs/src/types.js.map +1 -1
  51. package/dist/{index-CFZ7RDZ9.js → index-DZq20cR6.js} +1 -1
  52. package/dist/src/embed/app.d.ts +6 -1
  53. package/dist/src/embed/app.d.ts.map +1 -1
  54. package/dist/src/embed/conversation.d.ts +2 -1
  55. package/dist/src/embed/conversation.d.ts.map +1 -1
  56. package/dist/src/embed/liveboard.d.ts.map +1 -1
  57. package/dist/src/embed/search.d.ts +24 -1
  58. package/dist/src/embed/search.d.ts.map +1 -1
  59. package/dist/src/embed/spotter-utils.d.ts +3 -0
  60. package/dist/src/embed/spotter-utils.d.ts.map +1 -1
  61. package/dist/src/embed/ts-embed.d.ts +1 -0
  62. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  63. package/dist/src/index.d.ts +2 -2
  64. package/dist/src/index.d.ts.map +1 -1
  65. package/dist/src/mixpanel-service.d.ts.map +1 -1
  66. package/dist/src/test/test-utils.d.ts +1 -0
  67. package/dist/src/test/test-utils.d.ts.map +1 -1
  68. package/dist/src/types.d.ts +424 -0
  69. package/dist/src/types.d.ts.map +1 -1
  70. package/dist/tsembed-react.es.js +173 -13
  71. package/dist/tsembed-react.js +172 -12
  72. package/dist/tsembed.es.js +174 -14
  73. package/dist/tsembed.js +172 -12
  74. package/dist/visual-embed-sdk-react-full.d.ts +425 -0
  75. package/dist/visual-embed-sdk-react.d.ts +425 -0
  76. package/dist/visual-embed-sdk.d.ts +453 -0
  77. package/lib/package.json +1 -1
  78. package/lib/src/embed/app.d.ts +6 -1
  79. package/lib/src/embed/app.d.ts.map +1 -1
  80. package/lib/src/embed/app.js +4 -1
  81. package/lib/src/embed/app.js.map +1 -1
  82. package/lib/src/embed/app.spec.js +113 -1
  83. package/lib/src/embed/app.spec.js.map +1 -1
  84. package/lib/src/embed/conversation.d.ts +2 -1
  85. package/lib/src/embed/conversation.d.ts.map +1 -1
  86. package/lib/src/embed/conversation.js.map +1 -1
  87. package/lib/src/embed/liveboard.d.ts.map +1 -1
  88. package/lib/src/embed/liveboard.js +4 -1
  89. package/lib/src/embed/liveboard.js.map +1 -1
  90. package/lib/src/embed/liveboard.spec.js +22 -0
  91. package/lib/src/embed/liveboard.spec.js.map +1 -1
  92. package/lib/src/embed/search.d.ts +24 -1
  93. package/lib/src/embed/search.d.ts.map +1 -1
  94. package/lib/src/embed/search.js +15 -2
  95. package/lib/src/embed/search.js.map +1 -1
  96. package/lib/src/embed/search.spec.js +100 -1
  97. package/lib/src/embed/search.spec.js.map +1 -1
  98. package/lib/src/embed/spotter-utils.d.ts +3 -0
  99. package/lib/src/embed/spotter-utils.d.ts.map +1 -1
  100. package/lib/src/embed/spotter-utils.js +11 -3
  101. package/lib/src/embed/spotter-utils.js.map +1 -1
  102. package/lib/src/embed/spotter-utils.spec.js +51 -0
  103. package/lib/src/embed/spotter-utils.spec.js.map +1 -1
  104. package/lib/src/embed/ts-embed.d.ts +1 -0
  105. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  106. package/lib/src/embed/ts-embed.js +14 -2
  107. package/lib/src/embed/ts-embed.js.map +1 -1
  108. package/lib/src/embed/ts-embed.spec.js +139 -0
  109. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  110. package/lib/src/index.d.ts +2 -2
  111. package/lib/src/index.d.ts.map +1 -1
  112. package/lib/src/index.js +2 -2
  113. package/lib/src/index.js.map +1 -1
  114. package/lib/src/mixpanel-service.d.ts.map +1 -1
  115. package/lib/src/mixpanel-service.js +2 -0
  116. package/lib/src/mixpanel-service.js.map +1 -1
  117. package/lib/src/mixpanel-service.spec.js +2 -0
  118. package/lib/src/mixpanel-service.spec.js.map +1 -1
  119. package/lib/src/test/test-utils.d.ts +1 -0
  120. package/lib/src/test/test-utils.d.ts.map +1 -1
  121. package/lib/src/test/test-utils.js +25 -1
  122. package/lib/src/test/test-utils.js.map +1 -1
  123. package/lib/src/types.d.ts +424 -0
  124. package/lib/src/types.d.ts.map +1 -1
  125. package/lib/src/types.js +120 -0
  126. package/lib/src/types.js.map +1 -1
  127. package/lib/src/visual-embed-sdk.d.ts +453 -0
  128. package/package.json +1 -1
  129. package/src/embed/app.spec.ts +141 -0
  130. package/src/embed/app.ts +11 -0
  131. package/src/embed/conversation.ts +2 -1
  132. package/src/embed/liveboard.spec.ts +32 -0
  133. package/src/embed/liveboard.ts +5 -0
  134. package/src/embed/search.spec.ts +118 -0
  135. package/src/embed/search.ts +43 -1
  136. package/src/embed/spotter-utils.spec.ts +52 -0
  137. package/src/embed/spotter-utils.ts +19 -3
  138. package/src/embed/ts-embed.spec.ts +185 -0
  139. package/src/embed/ts-embed.ts +14 -3
  140. package/src/index.ts +16 -0
  141. package/src/mixpanel-service.spec.ts +2 -0
  142. package/src/mixpanel-service.ts +2 -0
  143. package/src/test/test-utils.ts +33 -1
  144. package/src/types.ts +454 -0
@@ -1,6 +1,6 @@
1
1
  import isUndefined from 'lodash/isUndefined';
2
2
  import { ERROR_MESSAGE } from '../errors';
3
- import { Param, BaseViewConfig, RuntimeFilter, RuntimeParameter, ErrorDetailsTypes, EmbedErrorCodes, DefaultAppInitData } from '../types';
3
+ import { Param, BaseViewConfig, RuntimeFilter, RuntimeParameter, ErrorDetailsTypes, EmbedErrorCodes, DefaultAppInitData, VisualizationOverrides } from '../types';
4
4
  import { TsEmbed } from './ts-embed';
5
5
  import { buildSpotterSidebarAppInitData } from './spotter-utils';
6
6
  import { getQueryParamString, getFilterQuery, getRuntimeParameters, setParamIfDefined } from '../utils';
@@ -360,6 +360,7 @@ export interface ConversationViewConfig extends SpotterEmbedViewConfig {}
360
360
  export interface SpotterAppInitData extends DefaultAppInitData {
361
361
  embedParams?: {
362
362
  spotterSidebarConfig?: SpotterSidebarViewConfig;
363
+ visualOverridesParams?: VisualizationOverrides | null;
363
364
  };
364
365
  }
365
366
 
@@ -634,6 +634,38 @@ describe('Liveboard/viz embed tests', () => {
634
634
  });
635
635
  });
636
636
 
637
+ test('Should add newChartsLibrary flag set to true to the iframe src', async () => {
638
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
639
+ ...defaultViewConfig,
640
+ liveboardId,
641
+ newChartsLibrary: true,
642
+ } as LiveboardViewConfig);
643
+
644
+ liveboardEmbed.render();
645
+ await executeAfterWait(() => {
646
+ expectUrlMatchesWithParams(
647
+ getIFrameSrc(),
648
+ `http://${thoughtSpotHost}/?embedApp=true${defaultParams}&muzeChartPhase1EnabledGA=true${prefixParams}#/embed/viz/${liveboardId}`,
649
+ );
650
+ });
651
+ });
652
+
653
+ test('Should add newChartsLibrary flag set to false to the iframe src', async () => {
654
+ const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
655
+ ...defaultViewConfig,
656
+ liveboardId,
657
+ newChartsLibrary: false,
658
+ } as LiveboardViewConfig);
659
+
660
+ liveboardEmbed.render();
661
+ await executeAfterWait(() => {
662
+ expectUrlMatchesWithParams(
663
+ getIFrameSrc(),
664
+ `http://${thoughtSpotHost}/?embedApp=true${defaultParams}&muzeChartPhase1EnabledGA=false${prefixParams}#/embed/viz/${liveboardId}`,
665
+ );
666
+ });
667
+ });
668
+
637
669
  test('Should add hideIrrelevantFiltersAtTabLevel flag to the iframe src', async () => {
638
670
  const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
639
671
  ...defaultViewConfig,
@@ -619,6 +619,7 @@ export class LiveboardEmbed extends V1Embed {
619
619
  hideIrrelevantChipsInLiveboardTabs = false,
620
620
  showMaskedFilterChip = false,
621
621
  isLiveboardMasterpiecesEnabled = false,
622
+ newChartsLibrary,
622
623
  isEnhancedFilterInteractivityEnabled = false,
623
624
  enableAskSage,
624
625
  enable2ColumnLayout,
@@ -762,6 +763,10 @@ export class LiveboardEmbed extends V1Embed {
762
763
  params[Param.EnableLiveboardDataCache] = enableLiveboardDataCache;
763
764
  }
764
765
 
766
+ if (newChartsLibrary !== undefined) {
767
+ params[Param.EnableNewChartLibrary] = newChartsLibrary;
768
+ }
769
+
765
770
  params[Param.LiveboardHeaderSticky] = isLiveboardHeaderSticky;
766
771
  params[Param.LiveboardHeaderV2] = isLiveboardCompactHeaderEnabled;
767
772
  params[Param.ShowLiveboardVerifiedBadge] = showLiveboardVerifiedBadge;
@@ -17,6 +17,7 @@ import {
17
17
  expectUrlMatchesWithParams,
18
18
  getIFrameEl,
19
19
  postMessageToParent,
20
+ testVisualOverridesInEmbed,
20
21
  } from '../test/test-utils';
21
22
  import { version } from '../../package.json';
22
23
  import { SearchBarEmbed } from './search-bar';
@@ -524,6 +525,34 @@ describe('Search embed tests', () => {
524
525
  });
525
526
  });
526
527
 
528
+ test('Should add newChartsLibrary flag set to true to the iframe src', async () => {
529
+ const searchEmbed = new SearchEmbed(getRootEl(), {
530
+ ...defaultViewConfig,
531
+ newChartsLibrary: true,
532
+ });
533
+ searchEmbed.render();
534
+ await executeAfterWait(() => {
535
+ expectUrlMatchesWithParams(
536
+ getIFrameSrc(),
537
+ `http://${thoughtSpotHost}/v2/?${defaultParamsWithHiddenActions}&enableDataPanelV2=true&muzeChartPhase1EnabledGA=true&dataSourceMode=expand&useLastSelectedSources=false${prefixParams}#/embed/answer`,
538
+ );
539
+ });
540
+ });
541
+
542
+ test('Should add newChartsLibrary flag set to false to the iframe src', async () => {
543
+ const searchEmbed = new SearchEmbed(getRootEl(), {
544
+ ...defaultViewConfig,
545
+ newChartsLibrary: false,
546
+ });
547
+ searchEmbed.render();
548
+ await executeAfterWait(() => {
549
+ expectUrlMatchesWithParams(
550
+ getIFrameSrc(),
551
+ `http://${thoughtSpotHost}/v2/?${defaultParamsWithHiddenActions}&enableDataPanelV2=true&muzeChartPhase1EnabledGA=false&dataSourceMode=expand&useLastSelectedSources=false${prefixParams}#/embed/answer`,
552
+ );
553
+ });
554
+ });
555
+
527
556
  test('should set dataPanelCustomGroupsAccordionInitialState to EXPAND_FIRST when passed', async () => {
528
557
  const searchEmbed = new SearchBarEmbed(getRootEl() as any, {
529
558
  ...defaultViewConfig,
@@ -811,3 +840,92 @@ describe('SearchBarEmbed tests', () => {
811
840
  });
812
841
  });
813
842
  });
843
+
844
+ describe('SearchEmbed visualOverrides tests', () => {
845
+ test('should pass visualOverrides in embedParams when visualOverrides config is provided', async () => {
846
+ const visualOverrides = {
847
+ chart: {
848
+ legend: {
849
+ show: true,
850
+ position: 'bottom' as const,
851
+ },
852
+ },
853
+ };
854
+ const searchEmbed = new SearchEmbed(getRootEl(), {
855
+ ...defaultViewConfig,
856
+ visualOverrides,
857
+ });
858
+
859
+ await testVisualOverridesInEmbed(searchEmbed, visualOverrides);
860
+ });
861
+
862
+ test('should not include visualOverridesParams when visualOverrides is not provided', async () => {
863
+ const searchEmbed = new SearchEmbed(getRootEl(), {
864
+ ...defaultViewConfig,
865
+ });
866
+ const mockEmbedEventPayload = {
867
+ type: EmbedEvent.APP_INIT,
868
+ data: {},
869
+ };
870
+
871
+ searchEmbed.render();
872
+
873
+ const mockPort: any = {
874
+ postMessage: jest.fn(),
875
+ };
876
+
877
+ await executeAfterWait(() => {
878
+ const iframe = getIFrameEl();
879
+ postMessageToParent(iframe.contentWindow, mockEmbedEventPayload, mockPort);
880
+ });
881
+
882
+ await executeAfterWait(() => {
883
+ const callArgs = mockPort.postMessage.mock.calls[0][0];
884
+ expect(callArgs.type).toBe(EmbedEvent.APP_INIT);
885
+ expect(callArgs.data.embedParams?.visualOverridesParams).toBeUndefined();
886
+ });
887
+ });
888
+
889
+ test('should pass visualOverrides with complex chart config', async () => {
890
+ const visualOverrides = {
891
+ chart: {
892
+ legend: {
893
+ show: true,
894
+ position: 'right' as const,
895
+ colorPalette: {
896
+ colors: ['#ff0000', '#00ff00', '#0000ff'],
897
+ },
898
+ },
899
+ columns: [
900
+ {
901
+ name: 'Revenue',
902
+ color: '#1f77b4',
903
+ },
904
+ ],
905
+ },
906
+ };
907
+ const searchEmbed = new SearchEmbed(getRootEl(), {
908
+ ...defaultViewConfig,
909
+ visualOverrides,
910
+ });
911
+
912
+ await testVisualOverridesInEmbed(searchEmbed, visualOverrides);
913
+ });
914
+
915
+ test('should pass visualOverrides with table config', async () => {
916
+ const visualOverrides = {
917
+ table: {
918
+ display: {
919
+ tableTheme: 'ZEBRA',
920
+ tableContentDensity: 'COMPACT',
921
+ },
922
+ },
923
+ };
924
+ const searchEmbed = new SearchEmbed(getRootEl(), {
925
+ ...defaultViewConfig,
926
+ visualOverrides,
927
+ });
928
+
929
+ await testVisualOverridesInEmbed(searchEmbed, visualOverrides);
930
+ });
931
+ });
@@ -15,6 +15,7 @@ import {
15
15
  SearchLiveboardCommonViewConfig,
16
16
  DefaultAppInitData,
17
17
  BaseViewConfig,
18
+ VisualizationOverrides,
18
19
  } from '../types';
19
20
  import {
20
21
  getQueryParamString,
@@ -317,6 +318,27 @@ export interface SearchViewConfig
317
318
  * ```
318
319
  */
319
320
  focusSearchBarOnRender?: boolean;
321
+ /**
322
+ * Enable or disable Muze chart phase 1 GA
323
+ *
324
+ * Supported embed types: `SearchEmbed`
325
+ * @version SDK: 1.49.0 | ThoughtSpot Cloud: 26.6.0.cl
326
+ * @default false
327
+ * @example
328
+ * ```js
329
+ * const embed = new SearchEmbed('#tsEmbed', {
330
+ * ... // other embed view config
331
+ * newChartsLibrary: true,
332
+ * })
333
+ * ```
334
+ */
335
+ newChartsLibrary?: boolean;
336
+
337
+ /**
338
+ * Visual overrides to customize the chart or table properties.
339
+ * @version SDK: 1.49.0 | ThoughtSpot: 26.6.0.cl
340
+ */
341
+ visualOverrides?: VisualizationOverrides;
320
342
  }
321
343
 
322
344
  export const HiddenActionItemByDefaultForSearchEmbed = [
@@ -329,6 +351,9 @@ export const HiddenActionItemByDefaultForSearchEmbed = [
329
351
 
330
352
  export interface SearchAppInitData extends DefaultAppInitData {
331
353
  searchOptions?: SearchOptions;
354
+ embedParams?: {
355
+ visualOverridesParams?: VisualizationOverrides | null;
356
+ };
332
357
  }
333
358
 
334
359
  /**
@@ -380,7 +405,19 @@ export class SearchEmbed extends TsEmbed {
380
405
 
381
406
  protected async getAppInitData(): Promise<SearchAppInitData> {
382
407
  const defaultAppInitData = await super.getAppInitData();
383
- return { ...defaultAppInitData, ...this.getSearchInitData() };
408
+ const result: SearchAppInitData = {
409
+ ...defaultAppInitData,
410
+ ...this.getSearchInitData(),
411
+ };
412
+
413
+ if (this.viewConfig.visualOverrides) {
414
+ result.embedParams = {
415
+ ...((defaultAppInitData as any).embedParams || {}),
416
+ visualOverridesParams: this.viewConfig.visualOverrides,
417
+ };
418
+ }
419
+
420
+ return result;
384
421
  }
385
422
 
386
423
  protected getEmbedParamsObject() {
@@ -405,6 +442,7 @@ export class SearchEmbed extends TsEmbed {
405
442
  excludeSearchTokenStringFromURL,
406
443
  collapseSearchBar = true,
407
444
  isThisPeriodInDateFiltersEnabled,
445
+ newChartsLibrary,
408
446
  } = this.viewConfig;
409
447
  const queryParams = this.getBaseQueryParams();
410
448
 
@@ -451,6 +489,10 @@ export class SearchEmbed extends TsEmbed {
451
489
  queryParams[Param.IsThisPeriodInDateFiltersEnabled] = isThisPeriodInDateFiltersEnabled;
452
490
  }
453
491
 
492
+ if (newChartsLibrary !== undefined) {
493
+ queryParams[Param.EnableNewChartLibrary] = newChartsLibrary;
494
+ }
495
+
454
496
  queryParams[Param.DataPanelV2Enabled] = dataPanelV2;
455
497
  queryParams[Param.DataSourceMode] = this.getDataSourceMode();
456
498
 
@@ -53,4 +53,56 @@ describe('buildSpotterSidebarAppInitData', () => {
53
53
  }));
54
54
  expect(result.embedParams?.spotterSidebarConfig?.spotterDocumentationUrl).toBeUndefined();
55
55
  });
56
+
57
+ it('returns base with visualOverridesParams when only visualOverrides is provided', () => {
58
+ const visualOverrides = {
59
+ chart: {
60
+ legend: { show: true, position: 'bottom' as const },
61
+ },
62
+ };
63
+ const result = buildSpotterSidebarAppInitData(base, {
64
+ visualOverrides,
65
+ }, noopError);
66
+ expect(result).toEqual({
67
+ ...base,
68
+ embedParams: { visualOverridesParams: visualOverrides },
69
+ });
70
+ });
71
+
72
+ it('includes visualOverridesParams with spotterSidebarConfig', () => {
73
+ const visualOverrides = {
74
+ table: {
75
+ display: { tableTheme: 'ZEBRA' },
76
+ },
77
+ };
78
+ const result = buildSpotterSidebarAppInitData(base, {
79
+ spotterSidebarConfig: { enablePastConversationsSidebar: true },
80
+ visualOverrides,
81
+ }, noopError);
82
+ expect(result.embedParams?.spotterSidebarConfig?.enablePastConversationsSidebar).toBe(true);
83
+ expect(result.embedParams?.visualOverridesParams).toEqual(visualOverrides);
84
+ });
85
+
86
+ it('includes visualOverridesParams with standalone enablePastConversationsSidebar flag', () => {
87
+ const visualOverrides = {
88
+ chart: {
89
+ legend: { show: false },
90
+ },
91
+ };
92
+ const result = buildSpotterSidebarAppInitData(base, {
93
+ enablePastConversationsSidebar: true,
94
+ visualOverrides,
95
+ }, noopError);
96
+ expect(result.embedParams?.spotterSidebarConfig?.enablePastConversationsSidebar).toBe(true);
97
+ expect(result.embedParams?.visualOverridesParams).toEqual(visualOverrides);
98
+ });
99
+
100
+ it('does not include visualOverridesParams when it is undefined', () => {
101
+ const result = buildSpotterSidebarAppInitData(base, {
102
+ spotterSidebarConfig: { enablePastConversationsSidebar: true },
103
+ visualOverrides: undefined,
104
+ }, noopError);
105
+ expect(result.embedParams?.visualOverridesParams).toBeUndefined();
106
+ expect(result.embedParams?.spotterSidebarConfig?.enablePastConversationsSidebar).toBe(true);
107
+ });
56
108
  });
@@ -2,6 +2,7 @@ import { DefaultAppInitData, ErrorDetailsTypes, EmbedErrorCodes } from '../types
2
2
  import { validateHttpUrl } from '../utils';
3
3
  import { ERROR_MESSAGE } from '../errors';
4
4
  import type { SpotterSidebarViewConfig } from './conversation';
5
+ import type { VisualizationOverrides } from '../types';
5
6
 
6
7
  /**
7
8
  * Resolves enablePastConversationsSidebar with
@@ -22,10 +23,16 @@ export function buildSpotterSidebarAppInitData<T extends DefaultAppInitData>(
22
23
  viewConfig: {
23
24
  spotterSidebarConfig?: SpotterSidebarViewConfig;
24
25
  enablePastConversationsSidebar?: boolean;
26
+ visualOverrides?: VisualizationOverrides;
25
27
  },
26
28
  handleError: (err: any) => void,
27
- ): T & { embedParams?: { spotterSidebarConfig?: SpotterSidebarViewConfig } } {
28
- const { spotterSidebarConfig, enablePastConversationsSidebar } = viewConfig;
29
+ ): T & {
30
+ embedParams?: {
31
+ spotterSidebarConfig?: SpotterSidebarViewConfig;
32
+ visualOverridesParams?: VisualizationOverrides | null;
33
+ };
34
+ } {
35
+ const { spotterSidebarConfig, enablePastConversationsSidebar, visualOverrides } = viewConfig;
29
36
 
30
37
  const resolvedEnablePastConversations = resolveEnablePastConversationsSidebar({
31
38
  spotterSidebarConfigValue: spotterSidebarConfig?.enablePastConversationsSidebar,
@@ -33,7 +40,15 @@ export function buildSpotterSidebarAppInitData<T extends DefaultAppInitData>(
33
40
  });
34
41
 
35
42
  const hasConfig = spotterSidebarConfig || resolvedEnablePastConversations !== undefined;
36
- if (!hasConfig) return defaultAppInitData;
43
+ if (!hasConfig) {
44
+ if (visualOverrides === undefined) {
45
+ return defaultAppInitData;
46
+ }
47
+ return {
48
+ ...defaultAppInitData,
49
+ embedParams: { visualOverridesParams: visualOverrides },
50
+ };
51
+ }
37
52
 
38
53
  const resolvedSidebarConfig: SpotterSidebarViewConfig = {
39
54
  ...spotterSidebarConfig,
@@ -60,6 +75,7 @@ export function buildSpotterSidebarAppInitData<T extends DefaultAppInitData>(
60
75
  embedParams: {
61
76
  ...((defaultAppInitData as any).embedParams || {}),
62
77
  spotterSidebarConfig: resolvedSidebarConfig,
78
+ ...(visualOverrides !== undefined ? { visualOverridesParams: visualOverrides } : {}),
63
79
  },
64
80
  };
65
81
  }
@@ -4504,4 +4504,189 @@ describe('ShowPreRender with UpdateEmbedParams', () => {
4504
4504
  expect(iframe.allow).toContain('clipboard-read');
4505
4505
  expect(iframe.allow).toContain('clipboard-write');
4506
4506
  });
4507
+
4508
+ describe('shouldSkipEvent', () => {
4509
+ beforeAll(() => {
4510
+ init({
4511
+ thoughtSpotHost: 'tshost',
4512
+ authType: AuthType.None,
4513
+ });
4514
+ });
4515
+
4516
+ // Matches the structure produced by createValidationError / embedErrorDetails
4517
+ const makeNestedValidationData = (message = 'invalid payload') => ({
4518
+ type: EmbedEvent.Error,
4519
+ data: {
4520
+ errorType: 'VALIDATION_ERROR',
4521
+ message,
4522
+ code: EmbedErrorCodes.HOST_EVENT_VALIDATION,
4523
+ error: message,
4524
+ },
4525
+ });
4526
+
4527
+ // Matches the flat structure where errorType sits at the top level of data
4528
+ const makeFlatValidationData = (message = 'invalid payload') => ({
4529
+ errorType: EmbedErrorCodes.HOST_EVENT_VALIDATION,
4530
+ message,
4531
+ code: EmbedErrorCodes.HOST_EVENT_VALIDATION,
4532
+ });
4533
+
4534
+ const makeEmbed = (viewConfig: Partial<SearchViewConfig>) => {
4535
+ const embed = new SearchEmbed(getRootEl(), {
4536
+ ...defaultViewConfig,
4537
+ ...viewConfig,
4538
+ });
4539
+ return embed;
4540
+ };
4541
+
4542
+ test('skips Error event and logs warning when useHostEventsV2 is true and shouldBypassPayloadValidation is true', () => {
4543
+ jest.spyOn(logger, 'warn');
4544
+ const errorHandler = jest.fn();
4545
+ const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: true });
4546
+ embed.on(EmbedEvent.Error, errorHandler);
4547
+
4548
+ (embed as any).executeCallbacks(EmbedEvent.Error, makeNestedValidationData());
4549
+
4550
+ expect(errorHandler).not.toHaveBeenCalled();
4551
+ expect(logger.warn).toHaveBeenCalledWith(
4552
+ 'Host Event Validation failed: invalid payload',
4553
+ );
4554
+ });
4555
+
4556
+ test('skips Error event when errorType is resolved from data.data.code (nested format)', () => {
4557
+ jest.spyOn(logger, 'warn');
4558
+ const errorHandler = jest.fn();
4559
+ const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: true });
4560
+ embed.on(EmbedEvent.Error, errorHandler);
4561
+
4562
+ (embed as any).executeCallbacks(EmbedEvent.Error, makeNestedValidationData('nested error'));
4563
+
4564
+ expect(errorHandler).not.toHaveBeenCalled();
4565
+ expect(logger.warn).toHaveBeenCalledWith(
4566
+ 'Host Event Validation failed: nested error',
4567
+ );
4568
+ });
4569
+
4570
+ test('skips Error event when errorType is resolved from data.errorType (flat format)', () => {
4571
+ jest.spyOn(logger, 'warn');
4572
+ const errorHandler = jest.fn();
4573
+ const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: true });
4574
+ embed.on(EmbedEvent.Error, errorHandler);
4575
+
4576
+ (embed as any).executeCallbacks(EmbedEvent.Error, makeFlatValidationData());
4577
+
4578
+ expect(errorHandler).not.toHaveBeenCalled();
4579
+ });
4580
+
4581
+ test('delivers Error event to handler when useHostEventsV2 is true and shouldBypassPayloadValidation is undefined', () => {
4582
+ const errorHandler = jest.fn();
4583
+ const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: undefined });
4584
+ embed.on(EmbedEvent.Error, errorHandler);
4585
+
4586
+ (embed as any).executeCallbacks(EmbedEvent.Error, makeNestedValidationData());
4587
+
4588
+ expect(errorHandler).toHaveBeenCalled();
4589
+ });
4590
+
4591
+ test('delivers Error event to handler when useHostEventsV2 is true and shouldBypassPayloadValidation is false', () => {
4592
+ const errorHandler = jest.fn();
4593
+ const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: false });
4594
+ embed.on(EmbedEvent.Error, errorHandler);
4595
+
4596
+ (embed as any).executeCallbacks(EmbedEvent.Error, makeNestedValidationData());
4597
+
4598
+ expect(errorHandler).toHaveBeenCalled();
4599
+ });
4600
+
4601
+ test('skips Error event when useHostEventsV2 is false regardless of shouldBypassPayloadValidation', () => {
4602
+ jest.spyOn(logger, 'warn');
4603
+ const errorHandler = jest.fn();
4604
+ const embed = makeEmbed({ useHostEventsV2: false, shouldBypassPayloadValidation: undefined });
4605
+ embed.on(EmbedEvent.Error, errorHandler);
4606
+
4607
+ (embed as any).executeCallbacks(EmbedEvent.Error, makeNestedValidationData());
4608
+
4609
+ expect(errorHandler).not.toHaveBeenCalled();
4610
+ expect(logger.warn).toHaveBeenCalledWith(
4611
+ 'Host Event Validation failed: invalid payload',
4612
+ );
4613
+ });
4614
+
4615
+ test('logs warning with undefined message when flat format has no nested data', () => {
4616
+ jest.spyOn(logger, 'warn');
4617
+ const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: true });
4618
+ embed.on(EmbedEvent.Error, jest.fn());
4619
+
4620
+ (embed as any).executeCallbacks(EmbedEvent.Error, makeFlatValidationData());
4621
+
4622
+ expect(logger.warn).toHaveBeenCalledWith('Host Event Validation failed: undefined');
4623
+ });
4624
+
4625
+ test('skips Error event when useHostEventsV2 is false and shouldBypassPayloadValidation is true', () => {
4626
+ jest.spyOn(logger, 'warn');
4627
+ const errorHandler = jest.fn();
4628
+ const embed = makeEmbed({ useHostEventsV2: false, shouldBypassPayloadValidation: true });
4629
+ embed.on(EmbedEvent.Error, errorHandler);
4630
+
4631
+ (embed as any).executeCallbacks(EmbedEvent.Error, makeNestedValidationData());
4632
+
4633
+ expect(errorHandler).not.toHaveBeenCalled();
4634
+ expect(logger.warn).toHaveBeenCalledWith('Host Event Validation failed: invalid payload');
4635
+ });
4636
+
4637
+ test('skips via handleError when shouldBypassPayloadValidation is true', () => {
4638
+ jest.spyOn(logger, 'warn');
4639
+ const errorHandler = jest.fn();
4640
+ const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: true });
4641
+ embed.on(EmbedEvent.Error, errorHandler);
4642
+
4643
+ (embed as any).handleError({
4644
+ type: EmbedEvent.Error,
4645
+ data: {
4646
+ errorType: 'VALIDATION_ERROR',
4647
+ message: 'bad payload',
4648
+ code: EmbedErrorCodes.HOST_EVENT_VALIDATION,
4649
+ error: 'bad payload',
4650
+ },
4651
+ });
4652
+
4653
+ expect(errorHandler).not.toHaveBeenCalled();
4654
+ expect(logger.warn).toHaveBeenCalledWith('Host Event Validation failed: bad payload');
4655
+ });
4656
+
4657
+ test('delivers Error event to EmbedEvent.ALL handler when not skipped', () => {
4658
+ const allHandler = jest.fn();
4659
+ const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: false });
4660
+ embed.on(EmbedEvent.ALL, allHandler);
4661
+
4662
+ (embed as any).executeCallbacks(EmbedEvent.Error, makeNestedValidationData());
4663
+
4664
+ expect(allHandler).toHaveBeenCalled();
4665
+ });
4666
+
4667
+ test('does not skip non-Error events even with HOST_EVENT_VALIDATION error code', () => {
4668
+ const customActionHandler = jest.fn();
4669
+ const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: false });
4670
+ embed.on(EmbedEvent.CustomAction, customActionHandler);
4671
+
4672
+ (embed as any).executeCallbacks(EmbedEvent.CustomAction, {
4673
+ data: { code: EmbedErrorCodes.HOST_EVENT_VALIDATION },
4674
+ });
4675
+
4676
+ expect(customActionHandler).toHaveBeenCalled();
4677
+ });
4678
+
4679
+ test('does not skip Error events with unrelated error codes', () => {
4680
+ const errorHandler = jest.fn();
4681
+ const embed = makeEmbed({ useHostEventsV2: true, shouldBypassPayloadValidation: false });
4682
+ embed.on(EmbedEvent.Error, errorHandler);
4683
+
4684
+ (embed as any).executeCallbacks(EmbedEvent.Error, {
4685
+ errorType: 'SOME_OTHER_ERROR',
4686
+ message: 'something else failed',
4687
+ });
4688
+
4689
+ expect(errorHandler).toHaveBeenCalled();
4690
+ });
4691
+ });
4507
4692
  });
@@ -67,7 +67,7 @@ import {
67
67
  } from '../types';
68
68
  import { uploadMixpanelEvent, MIXPANEL_EVENT } from '../mixpanel-service';
69
69
  import { processEventData, processAuthFailure } from '../utils/processData';
70
- import pkgInfo from '../../package.json';
70
+ import { version } from '../../package.json';
71
71
  import {
72
72
  getAuthPromise, renderInQueue, handleAuth, notifyAuthFailure,
73
73
  getInitPromise,
@@ -80,8 +80,6 @@ import { getPreauthInfo } from '../utils/sessionInfoService';
80
80
  import { HostEventClient } from './hostEventClient/host-event-client';
81
81
  import { getInterceptInitData, handleInterceptEvent, processApiInterceptResponse, processLegacyInterceptResponse } from '../api-intercept';
82
82
 
83
- const { version } = pkgInfo;
84
-
85
83
  /**
86
84
  * Global prefix for all ThoughtSpot postHash Params.
87
85
  */
@@ -1151,6 +1149,18 @@ export class TsEmbed {
1151
1149
  }
1152
1150
  }
1153
1151
 
1152
+ private shouldSkipEvent(eventType: EmbedEvent, data: any): boolean {
1153
+ const errorType = data?.errorType ?? data?.data?.code;
1154
+ if (
1155
+ eventType === EmbedEvent.Error
1156
+ && errorType === EmbedErrorCodes.HOST_EVENT_VALIDATION
1157
+ && (!getHostEventsConfig(this.viewConfig).useHostEventsV2 || getHostEventsConfig(this.viewConfig).shouldBypassPayloadValidation)
1158
+ ) {
1159
+ logger.warn(`Host Event Validation failed: ${data?.data?.message}`);
1160
+ return true;
1161
+ }
1162
+ return false;
1163
+ }
1154
1164
  /**
1155
1165
  * Executes all registered event handlers for a particular event type
1156
1166
  * @param eventType The event type
@@ -1162,6 +1172,7 @@ export class TsEmbed {
1162
1172
  data: any,
1163
1173
  eventPort?: MessagePort | void,
1164
1174
  ): void {
1175
+ if (this.shouldSkipEvent(eventType, data)) return;
1165
1176
  const eventHandlers = this.eventHandlerMap.get(eventType) || [];
1166
1177
  const allHandlers = this.eventHandlerMap.get(EmbedEvent.ALL) || [];
1167
1178
  const callbacks = [...eventHandlers, ...allHandlers];
package/src/index.ts CHANGED
@@ -71,6 +71,14 @@ import {
71
71
  ErrorDetailsTypes,
72
72
  ContextType,
73
73
  AutoMCPFrameRendererViewConfig,
74
+ LegendPosition,
75
+ BackgroundFormatType,
76
+ ConditionalFormattingComparisonType,
77
+ ConditionalFormattingOperator,
78
+ DataLabelFilterOperator,
79
+ TableTheme,
80
+ TableContentDensity,
81
+ VisualizationOverrides,
74
82
  } from './types';
75
83
  import { CustomCssVariables } from './css-variables';
76
84
  import { AnswerService, SessionInterface, UnderlyingDataPoint } from './utils/graphql/answerService/answerService';
@@ -165,6 +173,14 @@ export {
165
173
  EmbedErrorDetailsEvent,
166
174
  ErrorDetailsTypes,
167
175
  AutoMCPFrameRendererViewConfig,
176
+ VisualizationOverrides,
177
+ LegendPosition,
178
+ BackgroundFormatType,
179
+ ConditionalFormattingComparisonType,
180
+ ConditionalFormattingOperator,
181
+ DataLabelFilterOperator,
182
+ TableTheme,
183
+ TableContentDensity,
168
184
  };
169
185
 
170
186
  export { resetCachedAuthToken } from './authToken';
@@ -8,6 +8,7 @@ import {
8
8
  import { AuthType } from './types';
9
9
  import { SessionInfo } from './utils/sessionInfoService';
10
10
  import { logger } from './utils/logger';
11
+ import { version } from '../package.json';
11
12
 
12
13
  const config = {
13
14
  thoughtSpotHost: 'https://10.87.89.232',
@@ -60,6 +61,7 @@ describe('Unit test for mixpanel', () => {
60
61
  clusterName: sessionInfo.clusterName,
61
62
  releaseVersion: sessionInfo.releaseVersion,
62
63
  hostAppUrl: 'localhost',
64
+ sdkVersion: version,
63
65
  });
64
66
  expect(mixpanel.identify).not.toHaveBeenCalledWith(sessionInfo.userGUID);
65
67
  });