diff --git a/src/embed/bodyless-conversation.ts b/src/embed/bodyless-conversation.ts index 8e081c7b..bf560a9e 100644 --- a/src/embed/bodyless-conversation.ts +++ b/src/embed/bodyless-conversation.ts @@ -39,6 +39,13 @@ 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 +56,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 f9a73b80..139ba205 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,21 @@ export class SpotterEmbed extends TsEmbed { queryParams[Param.HideSampleQuestions] = !!hideSampleQuestions; } + return queryParams; + } + + public getIframeSrc(): string { + const { + worksheetId, + searchOptions, + runtimeFilters, + excludeRuntimeFiltersfromURL, + runtimeParameters, + excludeRuntimeParametersfromURL, + } = 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 1849edf2..f0774d56 100644 --- a/src/embed/liveboard.spec.ts +++ b/src/embed/liveboard.spec.ts @@ -657,6 +657,7 @@ describe('Liveboard/viz embed tests', () => { test('navigateToLiveboard should trigger the navigate event with the correct path', async (done) => { mockMessageChannel(); // mock getSessionInfo + mockGetSessionInfo(); const liveboardEmbed = new LiveboardEmbed(getRootEl(), { ...defaultViewConfig, @@ -684,6 +685,13 @@ describe('Liveboard/viz embed tests', () => { mockMessageChannel(); // mock getSessionInfo + jest.spyOn(SessionInfoService, 'getSessionInfo').mockResolvedValue({ + releaseVersion: '1.0.0', + userGUID: '1234567890', + currentOrgId: 1, + privileges: [], + mixpanelToken: '1234567890', + }); mockGetSessionInfo(); const liveboardEmbed = new LiveboardEmbed(getRootEl(), { @@ -764,6 +772,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/liveboard.ts b/src/embed/liveboard.ts index f39eb911..c45bd417 100644 --- a/src/embed/liveboard.ts +++ b/src/embed/liveboard.ts @@ -424,6 +424,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 { @@ -536,7 +542,7 @@ export class LiveboardEmbed extends V1Embed { params[Param.LiveboardXLSXCSVDownload] = !!liveboardXLSXCSVDownload; const queryParams = getQueryParamString(params, true); - return queryParams; + return params; } private getIframeSuffixSrc(liveboardId: string, vizId: string, activeTabId: string) { diff --git a/src/embed/sage.ts b/src/embed/sage.ts index 8b1c4d9b..34cdfb40 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); } @@ -194,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, 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 40f9a496..534db212 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.spec.ts b/src/embed/ts-embed.spec.ts index 1c8708d1..e3af45e6 100644 --- a/src/embed/ts-embed.spec.ts +++ b/src/embed/ts-embed.spec.ts @@ -2488,7 +2488,7 @@ describe('Unit test case for ts embed', () => { }); afterAll((): void => { - window.location = location; + window.location = location as any; }); it('get url params for TS', () => { diff --git a/src/embed/ts-embed.ts b/src/embed/ts-embed.ts index fd00a807..ec3b0498 100644 --- a/src/embed/ts-embed.ts +++ b/src/embed/ts-embed.ts @@ -199,11 +199,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; } @@ -494,10 +494,10 @@ export class TsEmbed { this.on(EmbedEvent.APP_INIT, this.appInitCb, { 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); }; @@ -520,6 +520,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 @@ -702,10 +708,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); @@ -1140,12 +1151,12 @@ 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; + public isEmbedContainerLoaded = false; /** * @hidden @@ -1191,7 +1202,7 @@ export class TsEmbed { } else { logger.debug('pushing callback to embedContainerReadyCallbacks', callback); this.embedContainerReadyCallbacks.push(callback); - } + } } protected createEmbedContainerHandler = (source: EmbedEvent.AuthInit | EmbedEvent.EmbedListenerReady) => () => { @@ -1279,6 +1290,7 @@ export class TsEmbed { * Creates the preRender shell * @param showPreRenderByDefault - Show the preRender after render, hidden by default */ + public async preRender(showPreRenderByDefault = false, replaceExistingPreRender = false): Promise { if (!this.viewConfig.preRenderId) { logger.error(ERROR_MESSAGE.PRERENDER_ID_MISSING); @@ -1413,8 +1425,14 @@ export class TsEmbed { return this.preRender(true); } this.validatePreRenderViewConfig(this.viewConfig); + logger.debug('triggering UpdateEmbedParams', this.viewConfig); + this.executeAfterEmbedContainerLoaded(() => { + this.trigger(HostEvent.UpdateEmbedParams, this.getUpdateEmbedParamsObject()); + }); } + this.beforePrerenderVisible(); + if (this.el) { this.syncPreRenderStyle(); if (!this.viewConfig.doNotTrackPreRenderSize) { @@ -1432,8 +1450,6 @@ export class TsEmbed { } } - this.beforePrerenderVisible(); - removeStyleProperties(this.preRenderWrapper, ['z-index', 'opacity', 'pointer-events']); this.subscribeToEvents(); diff --git a/src/types.ts b/src/types.ts index 210ee6e5..e242f11e 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; @@ -695,7 +695,7 @@ export interface EmbedConfig { } // eslint-disable-next-line @typescript-eslint/no-empty-object-type -export interface LayoutConfig {} +export interface LayoutConfig { } /** * Embedded iframe configuration @@ -1481,21 +1481,21 @@ 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; /** * This flag is used to enable or disable the XLSX/CSV download option for Liveboards. @@ -1515,7 +1515,7 @@ export interface LiveboardAppEmbedViewConfig { } -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) @@ -2774,25 +2774,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. @@ -4015,7 +4015,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 @@ -4169,6 +4169,17 @@ export enum HostEvent { * @version SDK: 1.41.0 | ThoughtSpot: 10.12.0.cl */ AskSpotter = 'AskSpotter', + + /** + * @hidden + * Triggers the update of the embed params. + * + * @example + * ```js + * liveboardEmbed.trigger(HostEvent.UpdateEmbedParams, viewConfig); + * ``` + */ + UpdateEmbedParams = 'updateEmbedParams', } /** @@ -5671,15 +5682,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 {