From b94d451b8816ae1d9eef7081a4ac9c4c1c1f66a7 Mon Sep 17 00:00:00 2001 From: sastaachar Date: Tue, 29 Jul 2025 18:09:54 +0530 Subject: [PATCH 01/14] SCAL-265064 : dynamic pre render --- src/embed/ts-embed.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/embed/ts-embed.ts b/src/embed/ts-embed.ts index 124c4d98..37b7b902 100644 --- a/src/embed/ts-embed.ts +++ b/src/embed/ts-embed.ts @@ -1395,6 +1395,7 @@ export class TsEmbed { return this.preRender(true); } this.validatePreRenderViewConfig(this.viewConfig); + this.trigger('updateEmbedParams' as any, this.viewConfig); } if (this.el) { From d1bd4d9f15b69ef4b375fc4f2eaccb01880fad8a Mon Sep 17 00:00:00 2001 From: sastaachar Date: Tue, 29 Jul 2025 21:17:15 +0530 Subject: [PATCH 02/14] SCAL-265064 : enum --- src/embed/ts-embed.ts | 2 +- src/types.ts | 109 +++++++++++++++++++++++------------------- 2 files changed, 61 insertions(+), 50 deletions(-) diff --git a/src/embed/ts-embed.ts b/src/embed/ts-embed.ts index 37b7b902..a182c86c 100644 --- a/src/embed/ts-embed.ts +++ b/src/embed/ts-embed.ts @@ -1395,7 +1395,7 @@ export class TsEmbed { return this.preRender(true); } this.validatePreRenderViewConfig(this.viewConfig); - this.trigger('updateEmbedParams' as any, this.viewConfig); + this.trigger(HostEvent.UpdateEmbedParams, this.viewConfig); } if (this.el) { diff --git a/src/types.ts b/src/types.ts index 6743d20d..c429457e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -231,12 +231,12 @@ export type DOMSelector = string | HTMLElement; * Use {@link CustomCssVariables} or css rules. */ export interface customCssInterface { - /** - * The custom css variables, which can be set. - * The variables are available in the {@link CustomCssVariables} - * interface. For more information, see - * link:https://developers.thoughtspot.com/docs/css-variables-reference[CSS variable reference]. - */ + /** + * The custom css variables, which can be set. + * The variables are available in the {@link CustomCssVariables} + * interface. For more information, see + * link:https://developers.thoughtspot.com/docs/css-variables-reference[CSS variable reference]. + */ variables?: CustomCssVariables; /** * Can be used to define a custom font face @@ -648,7 +648,7 @@ export interface EmbedConfig { * ``` * @version SDK 1.37.0 | ThoughtSpot: 10.8.0.cl */ - customVariablesForThirdPartyTools?: Record< string, any >; + customVariablesForThirdPartyTools?: Record; disablePreauthCache?: boolean; @@ -669,7 +669,7 @@ export interface EmbedConfig { } // eslint-disable-next-line @typescript-eslint/no-empty-object-type -export interface LayoutConfig {} +export interface LayoutConfig { } /** * Embedded iframe configuration @@ -1421,25 +1421,25 @@ export interface LiveboardAppEmbedViewConfig { * ``` */ enableAskSage?: boolean; - /** - * This flag is used to show or hide checkboxes for including or excluding - * the cover and filters pages in the Liveboard PDF. - * - * Supported embed types: `AppEmbed`, `LiveboardEmbed` - * @version SDK: 1.40.0 | ThoughtSpot:10.8.0.cl - * @example - * ```js - * // Replace with embed component name. For example, AppEmbed or LiveboardEmbed - * const embed = new ('#tsEmbed', { - * ... // other embed view config - * coverAndFilterOptionInPDF: false, - * }) - * ``` - */ + /** + * This flag is used to show or hide checkboxes for including or excluding + * the cover and filters pages in the Liveboard PDF. + * + * Supported embed types: `AppEmbed`, `LiveboardEmbed` + * @version SDK: 1.40.0 | ThoughtSpot:10.8.0.cl + * @example + * ```js + * // Replace with embed component name. For example, AppEmbed or LiveboardEmbed + * const embed = new ('#tsEmbed', { + * ... // other embed view config + * coverAndFilterOptionInPDF: false, + * }) + * ``` + */ coverAndFilterOptionInPDF?: boolean; } -export interface AllEmbedViewConfig extends BaseViewConfig, SearchLiveboardCommonViewConfig, HomePageConfig, LiveboardAppEmbedViewConfig {} +export interface AllEmbedViewConfig extends BaseViewConfig, SearchLiveboardCommonViewConfig, HomePageConfig, LiveboardAppEmbedViewConfig { } /** * MessagePayload: Embed event payload: message type, data and status (start/end) @@ -2693,25 +2693,25 @@ export enum EmbedEvent { * ``` * @version SDK: 1.37.0 | ThoughtSpot: 10.8.0.cl */ - TableVizRendered = 'TableVizRendered', - /** - * Emitted when the liveboard is created from pin modal or Liveboard list page. - * You can use this event as a hook to trigger - * other events on liveboard creation. - * - * ```js - * liveboardEmbed.on(EmbedEvent.CreateLiveboard, (payload) => { - * console.log('payload', payload); - * }) - *``` - * @version SDK : 1.37.0 | ThoughtSpot: 10.8.0.cl - */ + TableVizRendered = 'TableVizRendered', + /** + * Emitted when the liveboard is created from pin modal or Liveboard list page. + * You can use this event as a hook to trigger + * other events on liveboard creation. + * + * ```js + * liveboardEmbed.on(EmbedEvent.CreateLiveboard, (payload) => { + * console.log('payload', payload); + * }) + *``` + * @version SDK : 1.37.0 | ThoughtSpot: 10.8.0.cl + */ CreateLiveboard = 'createLiveboard', /** * Emitted when a user creates a Model. * @version SDK : 1.37.0 | ThoughtSpot: 10.8.0.cl */ - CreateModel = 'createModel', + CreateModel = 'createModel', /** * @hidden * Emitted when a user exits present mode. @@ -3923,7 +3923,7 @@ export enum HostEvent { *``` * @version SDK: 1.36.0 | ThoughtSpot: 10.6.0.cl */ - InfoSuccess = 'InfoSuccess', + InfoSuccess = 'InfoSuccess', /** * Trigger the save action for an Answer. * To programmatically save an answer without opening the @@ -4076,7 +4076,18 @@ export enum HostEvent { * ``` * @version SDK: 1.41.0 | ThoughtSpot: 10.12.0.cl */ - AskSpotter = 'AskSpotter', + AskSpotter = 'askSpotter', + + /** + * @hidden + * Triggers the update of the embed params. + * + * @example + * ```js + * liveboardEmbed.trigger(HostEvent.UpdateEmbedParams, viewConfig); + * ``` + */ + UpdateEmbedParams = 'updateEmbedParams', } /** @@ -5553,15 +5564,15 @@ export interface ColumnValue { [key: string]: any; }; value: - | string - | number - | boolean - | { - v: { - s: number; - e: number; - }; - }; + | string + | number + | boolean + | { + v: { + s: number; + e: number; + }; + }; } export interface VizPoint { From 4fa4079dfb89ae82e90b48f95e213dee7b92be76 Mon Sep 17 00:00:00 2001 From: sastaachar Date: Mon, 4 Aug 2025 18:50:16 +0530 Subject: [PATCH 03/14] SCAL-265064 : update --- src/embed/liveboard.ts | 7 +++-- src/embed/ts-embed.ts | 71 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/embed/liveboard.ts b/src/embed/liveboard.ts index 8a1a8143..2ed3b020 100644 --- a/src/embed/liveboard.ts +++ b/src/embed/liveboard.ts @@ -741,13 +741,16 @@ export class LiveboardEmbed extends V1Embed { return this; } - public navigateToLiveboard(liveboardId: string, vizId?: string, activeTabId?: string) { + public currentLiveboardId: string = this.viewConfig.liveboardId; + + public navigateToLiveboard(liveboardId: string, vizId?: string, activeTabId?: string, onNavigateCalled?: () => void) { const path = this.getIframeSuffixSrc(liveboardId, vizId, activeTabId); this.viewConfig.liveboardId = liveboardId; this.viewConfig.activeTabId = activeTabId; this.viewConfig.vizId = vizId; if (this.isRendered) { - this.trigger(HostEvent.Navigate, path.substring(1)); + console.log('navigating to liveboard', path.substring(1)); + this.triggerAfterLoad(HostEvent.Navigate, path.substring(1), onNavigateCalled); } else if (this.viewConfig.preRenderId) { this.preRender(true); } else { diff --git a/src/embed/ts-embed.ts b/src/embed/ts-embed.ts index a182c86c..34d90703 100644 --- a/src/embed/ts-embed.ts +++ b/src/embed/ts-embed.ts @@ -185,7 +185,10 @@ export class TsEmbed { */ private fullscreenChangeHandler: (() => void) | null = null; + public id: string; + constructor(domSelector: DOMSelector, viewConfig?: ViewConfig) { + this.id = Date.now().toString(); this.el = getDOMNode(domSelector); this.eventHandlerMap = new Map(); this.isError = false; @@ -310,6 +313,8 @@ export class TsEmbed { private subscribedListeners: Record = {}; + public isEmbedContainerLoaded = false; + /** * Adds a global event listener to window for "message" events. * ThoughtSpot detects if a particular event is targeted to this @@ -475,17 +480,71 @@ export class TsEmbed { notifyAuthFailure(AuthFailureType.IDLE_SESSION_TIMEOUT); }; + private pendingEvents: Array<{ eventType: HostEvent, data: TriggerPayload, onEventTriggered?: () => void }> = []; + + protected getPreRenderObj() { + const embedObj = (this.insertedDomEl as any)?.[this.embedNodeKey] as T; + if (embedObj === (this as any)) { + console.log('embedObj is same as this'); + return null; + } + return (this.insertedDomEl as any)?.[this.embedNodeKey] as T; + } + + private checkEmbedContainerLoaded() { + if (this.isEmbedContainerLoaded) return true; + + const preRenderObj = this.getPreRenderObj(); + if (preRenderObj && preRenderObj.isEmbedContainerLoaded) { + this.isEmbedContainerLoaded = true; + } + + console.log('checkEmbedContainerLoaded', this.isEmbedContainerLoaded); + + return this.isEmbedContainerLoaded; + } + + private executePendingEvents() { + console.log('executePendingEvents', this.pendingEvents); + setTimeout(() => { + this.pendingEvents.forEach((event) => { + console.log('executing event', event.eventType, event.data); + this.trigger(event.eventType, event.data); + event.onEventTriggered?.(); + }); + this.pendingEvents = []; + }, 1000); + } + protected triggerAfterLoad(eventType: HostEvent, data: TriggerPayload, onEventTriggered?: () => void) { + if (this.checkEmbedContainerLoaded()) { + console.log('triggerAfterLoad', eventType, data); + this.trigger(eventType, data); + onEventTriggered?.(); + } else { + console.log('pushing to pendingEvents', eventType, data, this.getPreRenderObj()); + this.pendingEvents.push({ eventType, data, onEventTriggered }); + console.log('pendingEvents', this.pendingEvents); + } + } + /** * Register APP_INIT event and sendback init payload */ private registerAppInit = () => { this.on(EmbedEvent.APP_INIT, this.appInitCb, { start: false }, true); + this.on(EmbedEvent.AuthInit, () => { + console.log('AuthInit', this.getPreRenderObj()); + this.isEmbedContainerLoaded = true; + console.log('isEmbedContainerLoaded', this.isEmbedContainerLoaded); + console.log('executePendingEvents', this.pendingEvents); + this.executePendingEvents(); + }, { start: false }, true); this.on(EmbedEvent.AuthExpire, this.updateAuthToken, { start: false }, true); this.on(EmbedEvent.IdleSessionTimeout, this.idleSessionTimeout, { start: false }, true); - - const embedListenerReadyHandler = this.createEmbedContainerHandler(EmbedEvent.EmbedListenerReady); + + const embedListenerReadyHandler = this.createEmbedContainerHandler(EmbedEvent.EmbedListenerReady); this.on(EmbedEvent.EmbedListenerReady, embedListenerReadyHandler, { start: false }, true); - + const authInitHandler = this.createEmbedContainerHandler(EmbedEvent.AuthInit); this.on(EmbedEvent.AuthInit, authInitHandler, { start: false }, true); }; @@ -1179,7 +1238,7 @@ export class TsEmbed { } else { logger.debug('pushing callback to embedContainerReadyCallbacks', callback); this.embedContainerReadyCallbacks.push(callback); - } + } } protected createEmbedContainerHandler = (source: EmbedEvent.AuthInit | EmbedEvent.EmbedListenerReady) => () => { @@ -1398,6 +1457,8 @@ export class TsEmbed { this.trigger(HostEvent.UpdateEmbedParams, this.viewConfig); } + this.beforePrerenderVisible(); + if (this.el) { this.syncPreRenderStyle(); if (!this.viewConfig.doNotTrackPreRenderSize) { @@ -1415,8 +1476,6 @@ export class TsEmbed { } } - this.beforePrerenderVisible(); - removeStyleProperties(this.preRenderWrapper, ['z-index', 'opacity', 'pointer-events']); this.subscribeToEvents(); From 048ec9b49bb2a19c960c4fc226f3961351dc94db Mon Sep 17 00:00:00 2001 From: sastaachar Date: Tue, 5 Aug 2025 17:29:31 +0530 Subject: [PATCH 04/14] SCAL-265064 : clean uo --- src/embed/bodyless-conversation.ts | 12 ++++++--- src/embed/conversation.ts | 15 ++++++++++-- src/embed/liveboard.spec.ts | 33 ++++++++++++++++++++++--- src/embed/liveboard.ts | 35 ++++++++++++++++++++++++--- src/embed/sage.ts | 17 ++++++++----- src/embed/search-bar.tsx | 21 ++++++++++------ src/embed/search.ts | 25 +++++++++++++------ src/embed/ts-embed.ts | 39 ++++++++++++++++-------------- 8 files changed, 147 insertions(+), 50 deletions(-) diff --git a/src/embed/bodyless-conversation.ts b/src/embed/bodyless-conversation.ts index 8e081c7b..986f6a22 100644 --- a/src/embed/bodyless-conversation.ts +++ b/src/embed/bodyless-conversation.ts @@ -39,6 +39,14 @@ export class ConversationMessage extends TsEmbed { super(container, viewConfig); } + protected getEmbedParamsObject() { + const queryParams = this.getBaseQueryParams(); + + queryParams[Param.HideActions] = [...(queryParams[Param.HideActions] ?? [])]; + queryParams[Param.isSpotterAgentEmbed] = true; + return queryParams; + } + public getIframeSrc() { const { sessionId, @@ -49,10 +57,8 @@ export class ConversationMessage extends TsEmbed { messageId, } = this.viewConfig; const path = 'conv-assist-answer'; - const queryParams = this.getBaseQueryParams(); + const queryParams = this.getEmbedParamsObject(); - queryParams[Param.HideActions] = [...(queryParams[Param.HideActions] ?? [])]; - queryParams[Param.isSpotterAgentEmbed] = true; let query = ''; const queryParamsString = getQueryParamString(queryParams, true); if (queryParamsString) { diff --git a/src/embed/conversation.ts b/src/embed/conversation.ts index a3fcdf9b..6971e565 100644 --- a/src/embed/conversation.ts +++ b/src/embed/conversation.ts @@ -196,7 +196,7 @@ export class SpotterEmbed extends TsEmbed { super(container, viewConfig); } - public getIframeSrc(): string { + protected getEmbedParamsObject() { const { worksheetId, searchOptions, @@ -210,7 +210,7 @@ export class SpotterEmbed extends TsEmbed { runtimeParameters, excludeRuntimeParametersfromURL, } = this.viewConfig; - const path = 'insights/conv-assist'; + if (!worksheetId) { this.handleError(ERROR_MESSAGE.SPOTTER_EMBED_WORKSHEED_ID_NOT_FOUND); } @@ -235,6 +235,17 @@ export class SpotterEmbed extends TsEmbed { queryParams[Param.HideSampleQuestions] = !!hideSampleQuestions; } + return queryParams; + } + + public getIframeSrc(): string { + const { + worksheetId, + searchOptions, + } = this.viewConfig; + const path = 'insights/conv-assist'; + const queryParams = this.getEmbedParamsObject(); + let query = ''; const queryParamsString = getQueryParamString(queryParams, true); if (queryParamsString) { diff --git a/src/embed/liveboard.spec.ts b/src/embed/liveboard.spec.ts index db881120..523dce1a 100644 --- a/src/embed/liveboard.spec.ts +++ b/src/embed/liveboard.spec.ts @@ -601,6 +601,14 @@ describe('Liveboard/viz embed tests', () => { test('navigateToLiveboard should trigger the navigate event with the correct path', async (done) => { mockMessageChannel(); + // mock getSessionInfo + jest.spyOn(SessionInfoService, 'getSessionInfo').mockResolvedValue({ + releaseVersion: '1.0.0', + userGUID: '1234567890', + currentOrgId: 1, + privileges: [], + mixpanelToken: '1234567890', + }); const liveboardEmbed = new LiveboardEmbed(getRootEl(), { ...defaultViewConfig, } as LiveboardViewConfig); @@ -611,16 +619,30 @@ describe('Liveboard/viz embed tests', () => { postMessageToParent(iframe.contentWindow, { type: EmbedEvent.APP_INIT, }); + postMessageToParent(iframe.contentWindow, { + type: EmbedEvent.AuthInit, + }); + liveboardEmbed.navigateToLiveboard('lb1', 'viz1'); }); + executeAfterWait(() => { - liveboardEmbed.navigateToLiveboard('lb1', 'viz1'); expect(onSpy).toHaveBeenCalledWith(HostEvent.Navigate, 'embed/viz/lb1/viz1'); done(); - }); + }, 1002); }); test('navigateToLiveboard with preRender', async (done) => { mockMessageChannel(); + + // mock getSessionInfo + jest.spyOn(SessionInfoService, 'getSessionInfo').mockResolvedValue({ + releaseVersion: '1.0.0', + userGUID: '1234567890', + currentOrgId: 1, + privileges: [], + mixpanelToken: '1234567890', + }); + const liveboardEmbed = new LiveboardEmbed(getRootEl(), { ...defaultViewConfig, preRenderId: 'test', @@ -632,12 +654,15 @@ describe('Liveboard/viz embed tests', () => { postMessageToParent(iframe.contentWindow, { type: EmbedEvent.APP_INIT, }); + postMessageToParent(iframe.contentWindow, { + type: EmbedEvent.AuthInit, + }); }); executeAfterWait(() => { liveboardEmbed.navigateToLiveboard('lb1', 'viz1'); expect(onSpy).toHaveBeenCalledWith(HostEvent.Navigate, 'embed/viz/lb1/viz1'); done(); - }); + }, 1002); }); test('should set runtime parametere values in url params', async () => { const liveboardEmbed = new LiveboardEmbed(getRootEl(), { @@ -822,7 +847,7 @@ describe('Liveboard/viz embed tests', () => { done(); }); }); - + test('it should navigateToLiveboard with liveboard id is not passed with AuthInit event', async (done) => { mockMessageChannel(); const consoleSpy = jest.spyOn(console, 'error'); diff --git a/src/embed/liveboard.ts b/src/embed/liveboard.ts index 2ed3b020..da24ce12 100644 --- a/src/embed/liveboard.ts +++ b/src/embed/liveboard.ts @@ -408,6 +408,12 @@ export class LiveboardEmbed extends V1Embed { * embedded Liveboard or visualization. */ protected getEmbedParams() { + const params = this.getEmbedParamsObject(); + const queryParams = getQueryParamString(params, true); + return queryParams; + } + + protected getEmbedParamsObject() { let params: any = {}; params = this.getBaseQueryParams(params); const { @@ -511,9 +517,33 @@ export class LiveboardEmbed extends V1Embed { params[Param.DataPanelV2Enabled] = dataPanelV2; params[Param.EnableCustomColumnGroups] = enableCustomColumnGroups; params[Param.CoverAndFilterOptionInPDF] = coverAndFilterOptionInPDF; - const queryParams = getQueryParamString(params, true); - return queryParams; + if (oAuthPollingInterval !== undefined) { + params[Param.OauthPollingInterval] = oAuthPollingInterval; + } + + if (isForceRedirect) { + params[Param.IsForceRedirect] = isForceRedirect; + } + + if (dataSourceId !== undefined) { + params[Param.DataSourceId] = dataSourceId; + } + + + if (isLiveboardStylingAndGroupingEnabled !== undefined) { + params[Param.IsLiveboardStylingAndGroupingEnabled] = isLiveboardStylingAndGroupingEnabled; + } + + params[Param.LiveboardHeaderSticky] = isLiveboardHeaderSticky; + params[Param.LiveboardHeaderV2] = isLiveboardCompactHeaderEnabled; + params[Param.ShowLiveboardVerifiedBadge] = showLiveboardVerifiedBadge; + params[Param.ShowLiveboardReverifyBanner] = showLiveboardReverifyBanner; + params[Param.HideIrrelevantFiltersInTab] = hideIrrelevantChipsInLiveboardTabs; + params[Param.DataPanelV2Enabled] = dataPanelV2; + params[Param.EnableCustomColumnGroups] = enableCustomColumnGroups; + params[Param.CoverAndFilterOptionInPDF] = coverAndFilterOptionInPDF; + return params; } private getIframeSuffixSrc(liveboardId: string, vizId: string, activeTabId: string) { @@ -749,7 +779,6 @@ export class LiveboardEmbed extends V1Embed { this.viewConfig.activeTabId = activeTabId; this.viewConfig.vizId = vizId; if (this.isRendered) { - console.log('navigating to liveboard', path.substring(1)); this.triggerAfterLoad(HostEvent.Navigate, path.substring(1), onNavigateCalled); } else if (this.viewConfig.preRenderId) { this.preRender(true); diff --git a/src/embed/sage.ts b/src/embed/sage.ts index 8b1c4d9b..317c7f12 100644 --- a/src/embed/sage.ts +++ b/src/embed/sage.ts @@ -153,12 +153,7 @@ export class SageEmbed extends V1Embed { super(domSelector, viewConfig); } - /** - * Constructs a map of parameters to be passed on to the - * embedded Eureka or Sage search page. - * @returns {string} query string - */ - protected getEmbedParams(): string { + protected getEmbedParamsObject() { const { disableWorksheetChange, hideWorksheetSelector, @@ -184,6 +179,16 @@ export class SageEmbed extends V1Embed { params[Param.IsProductTour] = !!isProductTour; params[Param.HideSageAnswerHeader] = !!hideSageAnswerHeader; + return params; + } + + /** + * Constructs a map of parameters to be passed on to the + * embedded Eureka or Sage search page. + * @returns {string} query string + */ + protected getEmbedParams(): string { + const params = this.getEmbedParamsObject(); return getQueryParamString(params, true); } diff --git a/src/embed/search-bar.tsx b/src/embed/search-bar.tsx index df45faea..bc5f0262 100644 --- a/src/embed/search-bar.tsx +++ b/src/embed/search-bar.tsx @@ -118,12 +118,7 @@ export class SearchBarEmbed extends TsEmbed { this.viewConfig = viewConfig; } - /** - * Construct the URL of the embedded ThoughtSpot search to be - * loaded in the iframe - * @param dataSources A list of data source GUIDs - */ - private getIFrameSrc() { + protected getEmbedParamsObject() { const { searchOptions, dataSource, @@ -131,7 +126,6 @@ export class SearchBarEmbed extends TsEmbed { useLastSelectedSources = false, excludeSearchTokenStringFromURL, } = this.viewConfig; - const path = 'search-bar-embed'; const queryParams = this.getBaseQueryParams(); queryParams[Param.HideActions] = [...(queryParams[Param.HideActions] ?? [])]; @@ -159,6 +153,19 @@ export class SearchBarEmbed extends TsEmbed { queryParams[Param.UseLastSelectedDataSource] = false; } queryParams[Param.searchEmbed] = true; + + return queryParams; + } + + /** + * Construct the URL of the embedded ThoughtSpot search to be + * loaded in the iframe + * @param dataSources A list of data source GUIDs + */ + private getIFrameSrc() { + const queryParams = this.getEmbedParamsObject(); + const path = 'search-bar-embed'; + let query = ''; const queryParamsString = getQueryParamString(queryParams, true); if (queryParamsString) { diff --git a/src/embed/search.ts b/src/embed/search.ts index e0c23211..3d804326 100644 --- a/src/embed/search.ts +++ b/src/embed/search.ts @@ -331,7 +331,7 @@ export const HiddenActionItemByDefaultForSearchEmbed = [ ]; export interface SearchAppInitData extends DefaultAppInitData { - searchOptions?: SearchOptions; + searchOptions?: SearchOptions; } /** @@ -381,7 +381,7 @@ export class SearchEmbed extends TsEmbed { return { ...defaultAppInitData, ...this.getSearchInitData() }; } - protected getEmbedParams(): string { + protected getEmbedParamsObject() { const { hideResults, enableSearchAssist, @@ -398,7 +398,7 @@ export class SearchEmbed extends TsEmbed { collapseSearchBarInitially = false, enableCustomColumnGroups = false, isOnBeforeGetVizDataInterceptEnabled = false, - /* eslint-disable-next-line max-len */ + dataPanelCustomGroupsAccordionInitialState = DataPanelCustomColumnGroupsAccordionState.EXPAND_ALL, focusSearchBarOnRender = true, excludeRuntimeParametersfromURL, @@ -443,7 +443,7 @@ export class SearchEmbed extends TsEmbed { } if (isOnBeforeGetVizDataInterceptEnabled) { - /* eslint-disable-next-line max-len */ + queryParams[Param.IsOnBeforeGetVizDataInterceptEnabled] = isOnBeforeGetVizDataInterceptEnabled; } @@ -460,7 +460,7 @@ export class SearchEmbed extends TsEmbed { } queryParams[Param.searchEmbed] = true; - /* eslint-disable-next-line max-len */ + queryParams[Param.CollapseSearchBarInitially] = collapseSearchBarInitially || collapseSearchBar; queryParams[Param.EnableCustomColumnGroups] = enableCustomColumnGroups; if (dataPanelCustomGroupsAccordionInitialState @@ -468,12 +468,23 @@ export class SearchEmbed extends TsEmbed { || dataPanelCustomGroupsAccordionInitialState === DataPanelCustomColumnGroupsAccordionState.EXPAND_FIRST ) { - /* eslint-disable-next-line max-len */ + queryParams[Param.DataPanelCustomGroupsAccordionInitialState] = dataPanelCustomGroupsAccordionInitialState; } else { - /* eslint-disable-next-line max-len */ + queryParams[Param.DataPanelCustomGroupsAccordionInitialState] = DataPanelCustomColumnGroupsAccordionState.EXPAND_ALL; } + return queryParams; + } + + protected getEmbedParams() { + const { + runtimeParameters, + runtimeFilters, + excludeRuntimeParametersfromURL, + excludeRuntimeFiltersfromURL, + } = this.viewConfig; + const queryParams = this.getEmbedParamsObject(); let query = ''; const queryParamsString = getQueryParamString(queryParams, true); if (queryParamsString) { diff --git a/src/embed/ts-embed.ts b/src/embed/ts-embed.ts index 34d90703..2f307329 100644 --- a/src/embed/ts-embed.ts +++ b/src/embed/ts-embed.ts @@ -203,9 +203,10 @@ export class TsEmbed { }); this.hostEventClient = new HostEventClient(this.iFrame); + const embedConfig = getEmbedConfig(); + this.embedConfig = embedConfig; this.isReadyForRenderPromise = getInitPromise().then(async () => { - const embedConfig = getEmbedConfig(); - this.embedConfig = embedConfig; + if (!embedConfig.authTriggerContainer && !embedConfig.useEventForSAMLPopup) { this.embedConfig.authTriggerContainer = domSelector; } @@ -482,13 +483,12 @@ export class TsEmbed { private pendingEvents: Array<{ eventType: HostEvent, data: TriggerPayload, onEventTriggered?: () => void }> = []; - protected getPreRenderObj() { + protected getPreRenderObj(): T { const embedObj = (this.insertedDomEl as any)?.[this.embedNodeKey] as T; if (embedObj === (this as any)) { - console.log('embedObj is same as this'); - return null; + logger.info('embedObj is same as this'); } - return (this.insertedDomEl as any)?.[this.embedNodeKey] as T; + return embedObj; } private checkEmbedContainerLoaded() { @@ -499,16 +499,13 @@ export class TsEmbed { this.isEmbedContainerLoaded = true; } - console.log('checkEmbedContainerLoaded', this.isEmbedContainerLoaded); - return this.isEmbedContainerLoaded; } private executePendingEvents() { - console.log('executePendingEvents', this.pendingEvents); + logger.debug('executePendingEvents', this.pendingEvents); setTimeout(() => { this.pendingEvents.forEach((event) => { - console.log('executing event', event.eventType, event.data); this.trigger(event.eventType, event.data); event.onEventTriggered?.(); }); @@ -517,13 +514,10 @@ export class TsEmbed { } protected triggerAfterLoad(eventType: HostEvent, data: TriggerPayload, onEventTriggered?: () => void) { if (this.checkEmbedContainerLoaded()) { - console.log('triggerAfterLoad', eventType, data); this.trigger(eventType, data); onEventTriggered?.(); } else { - console.log('pushing to pendingEvents', eventType, data, this.getPreRenderObj()); this.pendingEvents.push({ eventType, data, onEventTriggered }); - console.log('pendingEvents', this.pendingEvents); } } @@ -533,10 +527,7 @@ export class TsEmbed { private registerAppInit = () => { this.on(EmbedEvent.APP_INIT, this.appInitCb, { start: false }, true); this.on(EmbedEvent.AuthInit, () => { - console.log('AuthInit', this.getPreRenderObj()); this.isEmbedContainerLoaded = true; - console.log('isEmbedContainerLoaded', this.isEmbedContainerLoaded); - console.log('executePendingEvents', this.pendingEvents); this.executePendingEvents(); }, { start: false }, true); this.on(EmbedEvent.AuthExpire, this.updateAuthToken, { start: false }, true); @@ -567,6 +558,12 @@ export class TsEmbed { return `${basePath}#`; } + protected getUpdateEmbedParamsObject() { + let queryParams = this.getEmbedParamsObject(); + queryParams = { ...this.viewConfig, ...queryParams }; + return queryParams; + } + /** * Common query params set for all the embed modes. * @param queryParams @@ -749,10 +746,15 @@ export class TsEmbed { } protected getEmbedParams() { - const queryParams = this.getBaseQueryParams(); + const queryParams = this.getEmbedParamsObject(); return getQueryParamString(queryParams); } + protected getEmbedParamsObject() { + const params = this.getBaseQueryParams(); + return params; + } + protected getRootIframeSrc() { const query = this.getEmbedParams(); return this.getEmbedBasePath(query); @@ -1454,7 +1456,8 @@ export class TsEmbed { return this.preRender(true); } this.validatePreRenderViewConfig(this.viewConfig); - this.trigger(HostEvent.UpdateEmbedParams, this.viewConfig); + logger.debug('triggering UpdateEmbedParams', this.viewConfig); + this.triggerAfterLoad(HostEvent.UpdateEmbedParams, this.getUpdateEmbedParamsObject()); } this.beforePrerenderVisible(); From ca4efcf1229bbf3ae29d4990e8729a594960f23c Mon Sep 17 00:00:00 2001 From: sastaachar Date: Thu, 21 Aug 2025 16:32:30 +0530 Subject: [PATCH 05/14] rebase resolve --- src/embed/ts-embed.ts | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/src/embed/ts-embed.ts b/src/embed/ts-embed.ts index 2f307329..556ef6c7 100644 --- a/src/embed/ts-embed.ts +++ b/src/embed/ts-embed.ts @@ -483,25 +483,6 @@ export class TsEmbed { private pendingEvents: Array<{ eventType: HostEvent, data: TriggerPayload, onEventTriggered?: () => void }> = []; - protected getPreRenderObj(): T { - const embedObj = (this.insertedDomEl as any)?.[this.embedNodeKey] as T; - if (embedObj === (this as any)) { - logger.info('embedObj is same as this'); - } - return embedObj; - } - - private checkEmbedContainerLoaded() { - if (this.isEmbedContainerLoaded) return true; - - const preRenderObj = this.getPreRenderObj(); - if (preRenderObj && preRenderObj.isEmbedContainerLoaded) { - this.isEmbedContainerLoaded = true; - } - - return this.isEmbedContainerLoaded; - } - private executePendingEvents() { logger.debug('executePendingEvents', this.pendingEvents); setTimeout(() => { @@ -1189,13 +1170,6 @@ export class TsEmbed { } } - /** - * @hidden - * Internal state to track if the embed container is loaded. - * This is used to trigger events after the embed container is loaded. - */ - public isEmbedContainerLoaded = false; - /** * @hidden * Internal state to track the callbacks to be executed after the embed container From f3ef390be12986d71cb58226110061803a134452 Mon Sep 17 00:00:00 2001 From: sastaachar Date: Thu, 21 Aug 2025 16:35:20 +0530 Subject: [PATCH 06/14] use executeAfterLoad --- src/embed/ts-embed.ts | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/src/embed/ts-embed.ts b/src/embed/ts-embed.ts index 556ef6c7..d17bb99f 100644 --- a/src/embed/ts-embed.ts +++ b/src/embed/ts-embed.ts @@ -481,36 +481,11 @@ export class TsEmbed { notifyAuthFailure(AuthFailureType.IDLE_SESSION_TIMEOUT); }; - private pendingEvents: Array<{ eventType: HostEvent, data: TriggerPayload, onEventTriggered?: () => void }> = []; - - private executePendingEvents() { - logger.debug('executePendingEvents', this.pendingEvents); - setTimeout(() => { - this.pendingEvents.forEach((event) => { - this.trigger(event.eventType, event.data); - event.onEventTriggered?.(); - }); - this.pendingEvents = []; - }, 1000); - } - protected triggerAfterLoad(eventType: HostEvent, data: TriggerPayload, onEventTriggered?: () => void) { - if (this.checkEmbedContainerLoaded()) { - this.trigger(eventType, data); - onEventTriggered?.(); - } else { - this.pendingEvents.push({ eventType, data, onEventTriggered }); - } - } - /** * Register APP_INIT event and sendback init payload */ private registerAppInit = () => { this.on(EmbedEvent.APP_INIT, this.appInitCb, { start: false }, true); - this.on(EmbedEvent.AuthInit, () => { - this.isEmbedContainerLoaded = true; - this.executePendingEvents(); - }, { start: false }, true); this.on(EmbedEvent.AuthExpire, this.updateAuthToken, { start: false }, true); this.on(EmbedEvent.IdleSessionTimeout, this.idleSessionTimeout, { start: false }, true); @@ -1431,7 +1406,9 @@ export class TsEmbed { } this.validatePreRenderViewConfig(this.viewConfig); logger.debug('triggering UpdateEmbedParams', this.viewConfig); - this.triggerAfterLoad(HostEvent.UpdateEmbedParams, this.getUpdateEmbedParamsObject()); + this.executeAfterEmbedContainerLoaded(() => { + this.trigger(HostEvent.UpdateEmbedParams, this.getUpdateEmbedParamsObject()); + }); } this.beforePrerenderVisible(); From 844e640964fce68717e14796c40093c975bfdd01 Mon Sep 17 00:00:00 2001 From: sastaachar Date: Thu, 21 Aug 2025 16:37:26 +0530 Subject: [PATCH 07/14] clean up --- src/embed/ts-embed.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/embed/ts-embed.ts b/src/embed/ts-embed.ts index d17bb99f..b14eeec2 100644 --- a/src/embed/ts-embed.ts +++ b/src/embed/ts-embed.ts @@ -185,10 +185,7 @@ export class TsEmbed { */ private fullscreenChangeHandler: (() => void) | null = null; - public id: string; - constructor(domSelector: DOMSelector, viewConfig?: ViewConfig) { - this.id = Date.now().toString(); this.el = getDOMNode(domSelector); this.eventHandlerMap = new Map(); this.isError = false; @@ -202,11 +199,10 @@ export class TsEmbed { ...viewConfig, }); this.hostEventClient = new HostEventClient(this.iFrame); - - const embedConfig = getEmbedConfig(); - this.embedConfig = embedConfig; + this.isReadyForRenderPromise = getInitPromise().then(async () => { - + const embedConfig = getEmbedConfig(); + this.embedConfig = embedConfig; if (!embedConfig.authTriggerContainer && !embedConfig.useEventForSAMLPopup) { this.embedConfig.authTriggerContainer = domSelector; } From 299a06176eff147c58016b4305856f0a8552e6c8 Mon Sep 17 00:00:00 2001 From: sastaachar Date: Thu, 21 Aug 2025 16:39:13 +0530 Subject: [PATCH 08/14] SCAL-265064 : clean up --- src/embed/ts-embed.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/embed/ts-embed.ts b/src/embed/ts-embed.ts index b14eeec2..8e670f6f 100644 --- a/src/embed/ts-embed.ts +++ b/src/embed/ts-embed.ts @@ -199,7 +199,6 @@ export class TsEmbed { ...viewConfig, }); this.hostEventClient = new HostEventClient(this.iFrame); - this.isReadyForRenderPromise = getInitPromise().then(async () => { const embedConfig = getEmbedConfig(); this.embedConfig = embedConfig; @@ -310,8 +309,6 @@ export class TsEmbed { private subscribedListeners: Record = {}; - public isEmbedContainerLoaded = false; - /** * Adds a global event listener to window for "message" events. * ThoughtSpot detects if a particular event is targeted to this @@ -1141,6 +1138,13 @@ export class TsEmbed { } } + /** + * @hidden + * Internal state to track if the embed container is loaded. + * This is used to trigger events after the embed container is loaded. + */ + public isEmbedContainerLoaded = false; + /** * @hidden * Internal state to track the callbacks to be executed after the embed container From 581e64203292df9559644aa739a2033d0ce8b5f2 Mon Sep 17 00:00:00 2001 From: sastaachar Date: Thu, 21 Aug 2025 17:10:11 +0530 Subject: [PATCH 09/14] SCAL-265064 : more clean up --- src/embed/conversation.ts | 4 ++++ src/embed/liveboard.ts | 6 ++---- src/embed/sage.ts | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/embed/conversation.ts b/src/embed/conversation.ts index 6971e565..d79076ef 100644 --- a/src/embed/conversation.ts +++ b/src/embed/conversation.ts @@ -242,6 +242,10 @@ export class SpotterEmbed extends TsEmbed { const { worksheetId, searchOptions, + runtimeFilters, + excludeRuntimeFiltersfromURL, + runtimeParameters, + excludeRuntimeParametersfromURL, } = this.viewConfig; const path = 'insights/conv-assist'; const queryParams = this.getEmbedParamsObject(); diff --git a/src/embed/liveboard.ts b/src/embed/liveboard.ts index da24ce12..d20192ab 100644 --- a/src/embed/liveboard.ts +++ b/src/embed/liveboard.ts @@ -771,15 +771,13 @@ export class LiveboardEmbed extends V1Embed { return this; } - public currentLiveboardId: string = this.viewConfig.liveboardId; - - public navigateToLiveboard(liveboardId: string, vizId?: string, activeTabId?: string, onNavigateCalled?: () => void) { + public navigateToLiveboard(liveboardId: string, vizId?: string, activeTabId?: string) { const path = this.getIframeSuffixSrc(liveboardId, vizId, activeTabId); this.viewConfig.liveboardId = liveboardId; this.viewConfig.activeTabId = activeTabId; this.viewConfig.vizId = vizId; if (this.isRendered) { - this.triggerAfterLoad(HostEvent.Navigate, path.substring(1), onNavigateCalled); + this.trigger(HostEvent.Navigate, path.substring(1)); } else if (this.viewConfig.preRenderId) { this.preRender(true); } else { diff --git a/src/embed/sage.ts b/src/embed/sage.ts index 317c7f12..34cdfb40 100644 --- a/src/embed/sage.ts +++ b/src/embed/sage.ts @@ -199,7 +199,7 @@ export class SageEmbed extends V1Embed { */ public getIFrameSrc(): string { const path = 'eureka'; - const postHashObj = {}; + const postHashObj: Record = {}; const tsPostHashParams = this.getThoughtSpotPostUrlParams(); const { dataSource, searchOptions, From aa20340661438965aeee868aa1a1d466bca5d69b Mon Sep 17 00:00:00 2001 From: sastaachar Date: Thu, 21 Aug 2025 17:17:08 +0530 Subject: [PATCH 10/14] SCAL-265064 : lint --- src/embed/ts-embed.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embed/ts-embed.spec.ts b/src/embed/ts-embed.spec.ts index f8dd217e..268a5ff5 100644 --- a/src/embed/ts-embed.spec.ts +++ b/src/embed/ts-embed.spec.ts @@ -2354,7 +2354,7 @@ describe('Unit test case for ts embed', () => { }); afterAll((): void => { - window.location = location; + window.location = location as any; }); it('get url params for TS', () => { From 8280f4a558ef89f56058cdc0ff846b0b994c7b05 Mon Sep 17 00:00:00 2001 From: sastaachar Date: Thu, 21 Aug 2025 18:50:10 +0530 Subject: [PATCH 11/14] SCAL-265064 : pre render should wait for init --- src/embed/bodyless-conversation.ts | 1 - src/embed/liveboard.spec.ts | 1 - src/embed/ts-embed.ts | 5 +++++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/embed/bodyless-conversation.ts b/src/embed/bodyless-conversation.ts index 986f6a22..bf560a9e 100644 --- a/src/embed/bodyless-conversation.ts +++ b/src/embed/bodyless-conversation.ts @@ -41,7 +41,6 @@ export class ConversationMessage extends TsEmbed { protected getEmbedParamsObject() { const queryParams = this.getBaseQueryParams(); - queryParams[Param.HideActions] = [...(queryParams[Param.HideActions] ?? [])]; queryParams[Param.isSpotterAgentEmbed] = true; return queryParams; diff --git a/src/embed/liveboard.spec.ts b/src/embed/liveboard.spec.ts index 523dce1a..6a03ef5e 100644 --- a/src/embed/liveboard.spec.ts +++ b/src/embed/liveboard.spec.ts @@ -912,7 +912,6 @@ describe('Liveboard/viz embed tests', () => { done(); }, 1005); }); - }); describe('LazyLoadingForFullHeight functionality', () => { diff --git a/src/embed/ts-embed.ts b/src/embed/ts-embed.ts index 8e670f6f..716da688 100644 --- a/src/embed/ts-embed.ts +++ b/src/embed/ts-embed.ts @@ -1278,6 +1278,11 @@ export class TsEmbed { * @param showPreRenderByDefault - Show the preRender after render, hidden by default */ public async preRender(showPreRenderByDefault = false): Promise { + if (!getIsInitCalled()) { + logger.error(ERROR_MESSAGE.RENDER_CALLED_BEFORE_INIT); + } + await this.isReadyForRenderPromise; + if (!this.viewConfig.preRenderId) { logger.error(ERROR_MESSAGE.PRERENDER_ID_MISSING); return this; From 29404c1017d73fcf86fef774b0624158c4a9f921 Mon Sep 17 00:00:00 2001 From: sastaachar Date: Thu, 21 Aug 2025 19:09:43 +0530 Subject: [PATCH 12/14] SCAL-265064 : minor fix --- src/embed/liveboard.spec.ts | 6 ++++++ src/embed/ts-embed.ts | 12 ++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/embed/liveboard.spec.ts b/src/embed/liveboard.spec.ts index 6a03ef5e..d6233ddb 100644 --- a/src/embed/liveboard.spec.ts +++ b/src/embed/liveboard.spec.ts @@ -721,6 +721,12 @@ describe('Liveboard/viz embed tests', () => { }); describe('PreRender flow for liveboard embed', () => { + beforeAll(() => { + init({ + thoughtSpotHost: "http://tshost", + authType: AuthType.None, + }); + }); test('it should preRender generic with liveboard id is not passed', async (done) => { const consoleSpy = jest.spyOn(console, 'error'); const libEmbed = new LiveboardEmbed(getRootEl(), { diff --git a/src/embed/ts-embed.ts b/src/embed/ts-embed.ts index 716da688..f0391c13 100644 --- a/src/embed/ts-embed.ts +++ b/src/embed/ts-embed.ts @@ -198,10 +198,11 @@ export class TsEmbed { uploadMixpanelEvent(MIXPANEL_EVENT.VISUAL_SDK_EMBED_CREATE, { ...viewConfig, }); + const embedConfig = getEmbedConfig(); + this.embedConfig = embedConfig; + this.hostEventClient = new HostEventClient(this.iFrame); this.isReadyForRenderPromise = getInitPromise().then(async () => { - const embedConfig = getEmbedConfig(); - this.embedConfig = embedConfig; if (!embedConfig.authTriggerContainer && !embedConfig.useEventForSAMLPopup) { this.embedConfig.authTriggerContainer = domSelector; } @@ -1277,12 +1278,7 @@ export class TsEmbed { * Creates the preRender shell * @param showPreRenderByDefault - Show the preRender after render, hidden by default */ - public async preRender(showPreRenderByDefault = false): Promise { - if (!getIsInitCalled()) { - logger.error(ERROR_MESSAGE.RENDER_CALLED_BEFORE_INIT); - } - await this.isReadyForRenderPromise; - + public async preRender(showPreRenderByDefault = false): Promise { if (!this.viewConfig.preRenderId) { logger.error(ERROR_MESSAGE.PRERENDER_ID_MISSING); return this; From 01afe9be14c081d686af5d4319d817123c3547ef Mon Sep 17 00:00:00 2001 From: sastaachar Date: Thu, 21 Aug 2025 19:16:27 +0530 Subject: [PATCH 13/14] SCAL-265064 : ??? --- src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.ts b/src/types.ts index c429457e..b437e174 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4076,7 +4076,7 @@ export enum HostEvent { * ``` * @version SDK: 1.41.0 | ThoughtSpot: 10.12.0.cl */ - AskSpotter = 'askSpotter', + AskSpotter = 'AskSpotter', /** * @hidden From 13934d79e35bddb74d3df9d0b88a860d7c17a9a5 Mon Sep 17 00:00:00 2001 From: sastaachar Date: Wed, 3 Sep 2025 17:58:17 +0530 Subject: [PATCH 14/14] SCAL-265064 : lint --- src/embed/liveboard.ts | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/embed/liveboard.ts b/src/embed/liveboard.ts index d20192ab..759df364 100644 --- a/src/embed/liveboard.ts +++ b/src/embed/liveboard.ts @@ -518,31 +518,6 @@ export class LiveboardEmbed extends V1Embed { params[Param.EnableCustomColumnGroups] = enableCustomColumnGroups; params[Param.CoverAndFilterOptionInPDF] = coverAndFilterOptionInPDF; - if (oAuthPollingInterval !== undefined) { - params[Param.OauthPollingInterval] = oAuthPollingInterval; - } - - if (isForceRedirect) { - params[Param.IsForceRedirect] = isForceRedirect; - } - - if (dataSourceId !== undefined) { - params[Param.DataSourceId] = dataSourceId; - } - - - if (isLiveboardStylingAndGroupingEnabled !== undefined) { - params[Param.IsLiveboardStylingAndGroupingEnabled] = isLiveboardStylingAndGroupingEnabled; - } - - params[Param.LiveboardHeaderSticky] = isLiveboardHeaderSticky; - params[Param.LiveboardHeaderV2] = isLiveboardCompactHeaderEnabled; - params[Param.ShowLiveboardVerifiedBadge] = showLiveboardVerifiedBadge; - params[Param.ShowLiveboardReverifyBanner] = showLiveboardReverifyBanner; - params[Param.HideIrrelevantFiltersInTab] = hideIrrelevantChipsInLiveboardTabs; - params[Param.DataPanelV2Enabled] = dataPanelV2; - params[Param.EnableCustomColumnGroups] = enableCustomColumnGroups; - params[Param.CoverAndFilterOptionInPDF] = coverAndFilterOptionInPDF; return params; }