@thoughtspot/visual-embed-sdk
Version:
ThoughtSpot Embed SDK
404 lines • 17.3 kB
JavaScript
/**
* Copyright (c) 2022
*
* Full application embedding
* https://developers.thoughtspot.com/docs/?pageid=full-embed
* @summary Full app embed
* @module
* @author Ayon Ghosh <ayon.ghosh@thoughtspot.com>
*/
import { logger } from '../utils/logger';
import { calculateVisibleElementData, getQueryParamString } from '../utils';
import { Param, HostEvent, EmbedEvent, } from '../types';
import { V1Embed } from './ts-embed';
/**
* Pages within the ThoughtSpot app that can be embedded.
*/
export var Page;
(function (Page) {
/**
* Home page
*/
Page["Home"] = "home";
/**
* Search page
*/
Page["Search"] = "search";
/**
* Saved answers listing page
*/
Page["Answers"] = "answers";
/**
* Liveboards listing page
*/
Page["Liveboards"] = "liveboards";
/**
* @hidden
*/
Page["Pinboards"] = "pinboards";
/**
* Data management page
*/
Page["Data"] = "data";
/**
* SpotIQ listing page
*/
Page["SpotIQ"] = "insights";
/**
* Monitor Alerts Page
*/
Page["Monitor"] = "monitor";
})(Page || (Page = {}));
/**
* Define the initial state os column custom group accordions
* in data panel v2.
*/
export var DataPanelCustomColumnGroupsAccordionState;
(function (DataPanelCustomColumnGroupsAccordionState) {
/**
* Expand all the accordion initially in data panel v2.
*/
DataPanelCustomColumnGroupsAccordionState["EXPAND_ALL"] = "EXPAND_ALL";
/**
* Collapse all the accordions initially in data panel v2.
*/
DataPanelCustomColumnGroupsAccordionState["COLLAPSE_ALL"] = "COLLAPSE_ALL";
/**
* Expand the first accordion and collapse the rest.
*/
DataPanelCustomColumnGroupsAccordionState["EXPAND_FIRST"] = "EXPAND_FIRST";
})(DataPanelCustomColumnGroupsAccordionState || (DataPanelCustomColumnGroupsAccordionState = {}));
export var HomePageSearchBarMode;
(function (HomePageSearchBarMode) {
HomePageSearchBarMode["OBJECT_SEARCH"] = "objectSearch";
HomePageSearchBarMode["AI_ANSWER"] = "aiAnswer";
HomePageSearchBarMode["NONE"] = "none";
})(HomePageSearchBarMode || (HomePageSearchBarMode = {}));
/**
* Define the version of the primary navbar
* @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl
*/
export var PrimaryNavbarVersion;
(function (PrimaryNavbarVersion) {
/**
* Sliding (v3) introduces a new left-side navigation hub featuring a tab switcher,
* along with updates to the top navigation bar.
* It serves as the foundational version of the PrimaryNavBar.
*/
PrimaryNavbarVersion["Sliding"] = "v3";
})(PrimaryNavbarVersion || (PrimaryNavbarVersion = {}));
/**
* Define the version of the home page
* @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl
*/
export var HomePage;
(function (HomePage) {
/**
* Modular (v2) introduces the updated Modular Home Experience.
* It serves as the foundational version of the home page.
*/
HomePage["Modular"] = "v2";
})(HomePage || (HomePage = {}));
/**
* Define the version of the list page
* @version SDK: 1.40.0 | ThoughtSpot: 10.12.0.cl
*/
export var ListPage;
(function (ListPage) {
/**
* List (v2) is the traditional List Experience.
* It serves as the foundational version of the list page.
*/
ListPage["List"] = "v2";
/**
* ListWithUXChanges (v3) introduces the new updated list page with UX changes.
*/
ListPage["ListWithUXChanges"] = "v3";
})(ListPage || (ListPage = {}));
/**
* Embeds full ThoughtSpot experience in a host application.
* @group Embed components
*/
export class AppEmbed extends V1Embed {
constructor(domSelector, viewConfig) {
viewConfig.embedComponentType = 'AppEmbed';
super(domSelector, viewConfig);
this.defaultHeight = '100%';
this.sendFullHeightLazyLoadData = () => {
const data = calculateVisibleElementData(this.iFrame);
this.trigger(HostEvent.VisibleEmbedCoordinates, data);
};
/**
* This is a handler for the RequestVisibleEmbedCoordinates event.
* It is used to send the visible coordinates data to the host application.
* @param data The event payload
* @param responder The responder function
*/
this.requestVisibleEmbedCoordinatesHandler = (data, responder) => {
logger.info('Sending RequestVisibleEmbedCoordinates', data);
const visibleCoordinatesData = calculateVisibleElementData(this.iFrame);
responder({ type: EmbedEvent.RequestVisibleEmbedCoordinates, data: visibleCoordinatesData });
};
/**
* Set the iframe height as per the computed height received
* from the ThoughtSpot app.
* @param data The event payload
*/
this.updateIFrameHeight = (data) => {
var _a;
this.setIFrameHeight(Math.max(data.data, (_a = this.iFrame) === null || _a === void 0 ? void 0 : _a.scrollHeight));
this.sendFullHeightLazyLoadData();
};
this.embedIframeCenter = (data, responder) => {
const obj = this.getIframeCenter();
responder({ type: EmbedEvent.EmbedIframeCenter, data: obj });
};
this.setIframeHeightForNonEmbedLiveboard = (data) => {
const { height: frameHeight } = this.viewConfig.frameParams || {};
const liveboardRelatedRoutes = [
'/pinboard/',
'/insights/pinboard/',
'/schedules/',
'/embed/viz/',
'/embed/insights/viz/',
'/liveboard/',
'/insights/liveboard/',
'/tsl-editor/PINBOARD_ANSWER_BOOK/',
'/import-tsl/PINBOARD_ANSWER_BOOK/',
];
if (liveboardRelatedRoutes.some((path) => data.data.currentPath.startsWith(path))) {
// Ignore the height reset of the frame, if the navigation is
// only within the liveboard page.
return;
}
this.setIFrameHeight(frameHeight || this.defaultHeight);
};
if (this.viewConfig.fullHeight === true) {
this.on(EmbedEvent.RouteChange, this.setIframeHeightForNonEmbedLiveboard);
this.on(EmbedEvent.EmbedHeight, this.updateIFrameHeight);
this.on(EmbedEvent.EmbedIframeCenter, this.embedIframeCenter);
this.on(EmbedEvent.RequestVisibleEmbedCoordinates, this.requestVisibleEmbedCoordinatesHandler);
}
}
/**
* Constructs a map of parameters to be passed on to the
* embedded Liveboard or visualization.
*/
getEmbedParams() {
const { tag, hideObjects, liveboardV2, showPrimaryNavbar, disableProfileAndHelp, hideHamburger, hideObjectSearch, hideNotification, hideApplicationSwitcher, hideOrgSwitcher, enableSearchAssist, fullHeight, dataPanelV2 = false, hideLiveboardHeader = false, showLiveboardTitle = true, showLiveboardDescription = true, hideHomepageLeftNav = false, modularHomeExperience = false, isLiveboardHeaderSticky = true, enableAskSage, collapseSearchBarInitially = false, enable2ColumnLayout, enableCustomColumnGroups = false, isOnBeforeGetVizDataInterceptEnabled = false, dataPanelCustomGroupsAccordionInitialState = DataPanelCustomColumnGroupsAccordionState.EXPAND_ALL, collapseSearchBar = true, isLiveboardCompactHeaderEnabled = false, showLiveboardVerifiedBadge = true, showLiveboardReverifyBanner = true, hideIrrelevantChipsInLiveboardTabs = false, homePageSearchBarMode, isUnifiedSearchExperienceEnabled = true, enablePendoHelp = true, discoveryExperience, coverAndFilterOptionInPDF = false, isLiveboardStylingAndGroupingEnabled, } = this.viewConfig;
let params = {};
params[Param.PrimaryNavHidden] = !showPrimaryNavbar;
params[Param.HideProfleAndHelp] = !!disableProfileAndHelp;
params[Param.HideApplicationSwitcher] = !!hideApplicationSwitcher;
params[Param.HideOrgSwitcher] = !!hideOrgSwitcher;
params[Param.HideLiveboardHeader] = hideLiveboardHeader;
params[Param.ShowLiveboardTitle] = showLiveboardTitle;
params[Param.ShowLiveboardDescription] = !!showLiveboardDescription;
params[Param.LiveboardHeaderSticky] = isLiveboardHeaderSticky;
params[Param.IsFullAppEmbed] = true;
params[Param.LiveboardHeaderV2] = isLiveboardCompactHeaderEnabled;
params[Param.ShowLiveboardVerifiedBadge] = showLiveboardVerifiedBadge;
params[Param.ShowLiveboardReverifyBanner] = showLiveboardReverifyBanner;
params[Param.HideIrrelevantFiltersInTab] = hideIrrelevantChipsInLiveboardTabs;
params[Param.IsUnifiedSearchExperienceEnabled] = isUnifiedSearchExperienceEnabled;
params[Param.CoverAndFilterOptionInPDF] = !!coverAndFilterOptionInPDF;
params = this.getBaseQueryParams(params);
if (hideObjectSearch) {
params[Param.HideObjectSearch] = !!hideObjectSearch;
}
if (hideHamburger) {
params[Param.HideHamburger] = !!hideHamburger;
}
if (hideNotification) {
params[Param.HideNotification] = !!hideNotification;
}
if (fullHeight === true) {
params[Param.fullHeight] = true;
if (this.viewConfig.lazyLoadingForFullHeight) {
params[Param.IsLazyLoadingForEmbedEnabled] = true;
params[Param.RootMarginForLazyLoad] = this.viewConfig.lazyLoadingMargin;
}
}
if (tag) {
params[Param.Tag] = tag;
}
if (hideObjects && hideObjects.length) {
params[Param.HideObjects] = JSON.stringify(hideObjects);
}
if (liveboardV2 !== undefined) {
params[Param.LiveboardV2Enabled] = liveboardV2;
}
if (enableSearchAssist !== undefined) {
params[Param.EnableSearchAssist] = enableSearchAssist;
}
if (enable2ColumnLayout !== undefined) {
params[Param.Enable2ColumnLayout] = enable2ColumnLayout;
}
if (enableAskSage) {
params[Param.enableAskSage] = enableAskSage;
}
if (isOnBeforeGetVizDataInterceptEnabled) {
params[Param.IsOnBeforeGetVizDataInterceptEnabled] = isOnBeforeGetVizDataInterceptEnabled;
}
if (homePageSearchBarMode) {
params[Param.HomePageSearchBarMode] = homePageSearchBarMode;
}
if (enablePendoHelp !== undefined) {
params[Param.EnablePendoHelp] = enablePendoHelp;
}
if (isLiveboardStylingAndGroupingEnabled !== undefined) {
params[Param.IsLiveboardStylingAndGroupingEnabled] = isLiveboardStylingAndGroupingEnabled;
}
params[Param.DataPanelV2Enabled] = dataPanelV2;
params[Param.HideHomepageLeftNav] = hideHomepageLeftNav;
params[Param.ModularHomeExperienceEnabled] = modularHomeExperience;
params[Param.CollapseSearchBarInitially] = collapseSearchBarInitially || collapseSearchBar;
params[Param.EnableCustomColumnGroups] = enableCustomColumnGroups;
if (dataPanelCustomGroupsAccordionInitialState
=== DataPanelCustomColumnGroupsAccordionState.COLLAPSE_ALL
|| dataPanelCustomGroupsAccordionInitialState
=== DataPanelCustomColumnGroupsAccordionState.EXPAND_FIRST) {
params[Param.DataPanelCustomGroupsAccordionInitialState] = dataPanelCustomGroupsAccordionInitialState;
}
else {
params[Param.DataPanelCustomGroupsAccordionInitialState] = DataPanelCustomColumnGroupsAccordionState.EXPAND_ALL;
}
if (discoveryExperience) {
// primaryNavbarVersion v3 will enabled the new left navigation
if (discoveryExperience.primaryNavbarVersion === PrimaryNavbarVersion.Sliding) {
params[Param.NavigationVersion] = discoveryExperience.primaryNavbarVersion;
}
// homePage v2 will enable the modular home page
// and it will override the modularHomeExperience value
if (discoveryExperience.homePage === HomePage.Modular) {
params[Param.ModularHomeExperienceEnabled] = true;
}
// listPageVersion v3 will enable the new list page
if (discoveryExperience.listPageVersion === ListPage.ListWithUXChanges) {
params[Param.ListPageVersion] = discoveryExperience.listPageVersion;
}
}
const queryParams = getQueryParamString(params, true);
return queryParams;
}
/**
* Constructs the URL of the ThoughtSpot app page to be rendered.
* @param pageId The ID of the page to be embedded.
*/
getIFrameSrc() {
const { pageId, path, modularHomeExperience } = this.viewConfig;
const pageRoute = this.formatPath(path) || this.getPageRoute(pageId, modularHomeExperience);
let url = `${this.getRootIframeSrc()}/${pageRoute}`;
const tsPostHashParams = this.getThoughtSpotPostUrlParams();
url = `${url}${tsPostHashParams}`;
return url;
}
/**
* Gets the ThoughtSpot route of the page for a particular page ID.
* @param pageId The identifier for a page in the ThoughtSpot app.
* @param modularHomeExperience
*/
getPageRoute(pageId, modularHomeExperience = false) {
switch (pageId) {
case Page.Search:
return 'answer';
case Page.Answers:
return modularHomeExperience ? 'home/answers' : 'answers';
case Page.Liveboards:
return modularHomeExperience ? 'home/liveboards' : 'pinboards';
case Page.Pinboards:
return modularHomeExperience ? 'home/liveboards' : 'pinboards';
case Page.Data:
return 'data/tables';
case Page.SpotIQ:
return modularHomeExperience ? 'home/spotiq-analysis' : 'insights/results';
case Page.Monitor:
return modularHomeExperience ? 'home/monitor-alerts' : 'insights/monitor-alerts';
case Page.Home:
default:
return 'home';
}
}
/**
* Formats the path provided by the user.
* @param path The URL path.
* @returns The URL path that the embedded app understands.
*/
formatPath(path) {
if (!path) {
return null;
}
// remove leading slash
if (path.indexOf('/') === 0) {
return path.substring(1);
}
return path;
}
/**
* Navigate to particular page for app embed. eg:answers/pinboards/home
* This is used for embedding answers, pinboards, visualizations and full application
* only.
* @param path string | number The string, set to iframe src and navigate to new page
* eg: appEmbed.navigateToPage('pinboards')
* When used with `noReload` (default: true) this can also be a number
* like 1/-1 to go forward/back.
* @param noReload boolean Trigger the navigation without reloading the page
* @version SDK: 1.12.0 | ThoughtSpot: 8.4.0.cl, 8.4.1-sw
*/
navigateToPage(path, noReload = false) {
if (!this.iFrame) {
logger.log('Please call render before invoking this method');
return;
}
if (noReload) {
this.trigger(HostEvent.Navigate, path);
}
else {
if (typeof path !== 'string') {
logger.warn('Path can only by a string when triggered without noReload');
return;
}
const iframeSrc = this.iFrame.src;
const embedPath = '#/embed';
const currentPath = iframeSrc.includes(embedPath) ? embedPath : '#';
this.iFrame.src = `${iframeSrc.split(currentPath)[0]}${currentPath}/${path.replace(/^\/?#?\//, '')}`;
}
}
/**
* Destroys the ThoughtSpot embed, and remove any nodes from the DOM.
* @version SDK: 1.39.0 | ThoughtSpot: 10.10.0.cl
*/
destroy() {
super.destroy();
this.unregisterLazyLoadEvents();
}
postRender() {
this.registerLazyLoadEvents();
}
registerLazyLoadEvents() {
if (this.viewConfig.fullHeight && this.viewConfig.lazyLoadingForFullHeight) {
// TODO: Use passive: true, install modernizr to check for passive
window.addEventListener('resize', this.sendFullHeightLazyLoadData);
window.addEventListener('scroll', this.sendFullHeightLazyLoadData, true);
}
}
unregisterLazyLoadEvents() {
if (this.viewConfig.fullHeight && this.viewConfig.lazyLoadingForFullHeight) {
window.removeEventListener('resize', this.sendFullHeightLazyLoadData);
window.removeEventListener('scroll', this.sendFullHeightLazyLoadData);
}
}
/**
* Renders the embedded application pages in the ThoughtSpot app.
* @param renderOptions An object containing the page ID
* to be embedded.
*/
async render() {
await super.render();
const src = this.getIFrameSrc();
await this.renderV1Embed(src);
this.postRender();
return this;
}
}
//# sourceMappingURL=app.js.map