UNPKG

@thoughtspot/visual-embed-sdk

Version:
404 lines 17.3 kB
/** * 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