Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions src/embed/app.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1709,6 +1709,116 @@ describe('App embed tests', () => {
'Please call render before invoking this method',
);
});

describe('overrideHistoryState flag', () => {
test('navigateToPage with overrideHistoryState=true should trigger HostEvent.Navigate', async () => {
mockMessageChannel();
const appEmbed = new AppEmbed(getRootEl(), {
frameParams: {
width: '100%',
height: '100%',
},
overrideHistoryState: true,
});
await appEmbed.render();

const iframe = getIFrameEl();
iframe.contentWindow.postMessage = jest.fn();
appEmbed.navigateToPage(path, false);

expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
expect.objectContaining({
type: HostEvent.Navigate,
data: path,
}),
`http://${thoughtSpotHost}`,
expect.anything(),
);
});

test('navigateToPage with overrideHistoryState=true and path as number should trigger HostEvent.Navigate', async () => {
mockMessageChannel();
const appEmbed = new AppEmbed(getRootEl(), {
frameParams: {
width: '100%',
height: '100%',
},
overrideHistoryState: true,
});
await appEmbed.render();

const iframe = getIFrameEl();
iframe.contentWindow.postMessage = jest.fn();
appEmbed.navigateToPage(-1, false);

expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
expect.objectContaining({
type: HostEvent.Navigate,
data: -1,
}),
`http://${thoughtSpotHost}`,
expect.anything(),
);
});

test('navigateToPage with overrideHistoryState=false and noReload=false should update iframe src', async () => {
const appEmbed = new AppEmbed(getRootEl(), {
frameParams: {
width: '100%',
height: '100%',
},
overrideHistoryState: false,
});
await appEmbed.render();
appEmbed.navigateToPage(path, false);

expectUrlMatchesWithParams(
getIFrameSrc(),
`http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}${defaultParamsPost}#/${path}`,
);
});

test('navigateToPage with overrideHistoryState=undefined and noReload=false should update iframe src', async () => {
const appEmbed = new AppEmbed(getRootEl(), {
frameParams: {
width: '100%',
height: '100%',
},
});
await appEmbed.render();
appEmbed.navigateToPage(path, false);

expectUrlMatchesWithParams(
getIFrameSrc(),
`http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}${defaultParamsPost}#/${path}`,
);
});

test('navigateToPage respects noReload priority over overrideHistoryState', async () => {
mockMessageChannel();
const appEmbed = new AppEmbed(getRootEl(), {
frameParams: {
width: '100%',
height: '100%',
},
overrideHistoryState: false,
});
await appEmbed.render();

const iframe = getIFrameEl();
iframe.contentWindow.postMessage = jest.fn();
appEmbed.navigateToPage(path, true);

expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
expect.objectContaining({
type: HostEvent.Navigate,
data: path,
}),
`http://${thoughtSpotHost}`,
expect.anything(),
);
});
});
});

describe('LazyLoadingForFullHeight functionality', () => {
Expand Down
3 changes: 2 additions & 1 deletion src/embed/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@
isPNGInScheduledEmailsEnabled?: boolean;

/**
* Enables the 'what you see is what you get' PDF export for Liveboards. Each tab is rendered on a single page

Check warning on line 617 in src/embed/app.ts

View workflow job for this annotation

GitHub Actions / build

Comments may not exceed 90 characters
* following the exact UI layout, instead of splitting visualizations across multiple A4 pages.
* This feature is GA from version 26.5.0.cl. It is disabled by default in embed deployments.
*
Expand Down Expand Up @@ -1203,7 +1203,7 @@

private sendFullHeightLazyLoadData = () => {
const data = calculateVisibleElementData(this.iFrame);
// this should be fired only if the lazyLoadingForFullHeight and fullHeight are true

Check warning on line 1206 in src/embed/app.ts

View workflow job for this annotation

GitHub Actions / build

Comments may not exceed 80 characters
if(this.viewConfig.lazyLoadingForFullHeight && this.viewConfig.fullHeight){
this.trigger(HostEvent.VisibleEmbedCoordinates, data);
}
Expand Down Expand Up @@ -1335,7 +1335,8 @@
logger.log('Please call render before invoking this method');
return;
}
if (noReload) {
const overrideHistoryState = this.viewConfig?.overrideHistoryState;
if (noReload || overrideHistoryState) {
this.trigger(HostEvent.Navigate, path);
} else {
if (typeof path !== 'string') {
Expand Down
40 changes: 40 additions & 0 deletions src/embed/ts-embed.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2281,6 +2281,46 @@ describe('Unit test case for ts embed', () => {
});
});

it('Sets the overrideHistoryState param', async () => {
const appEmbed = new AppEmbed(getRootEl(), {
frameParams: {
width: '100%',
height: '100%',
},
overrideHistoryState: true,
});
await appEmbed.render();
expectUrlToHaveParamsWithValues(getIFrameSrc(), {
overrideHistoryState: true,
});
});

it('Sets the overrideHistoryState param to false', async () => {
const appEmbed = new AppEmbed(getRootEl(), {
frameParams: {
width: '100%',
height: '100%',
},
overrideHistoryState: false,
});
await appEmbed.render();
expectUrlToHaveParamsWithValues(getIFrameSrc(), {
overrideHistoryState: false,
});
});

it('Should not add overrideHistoryState param when it is undefined', async () => {
const appEmbed = new AppEmbed(getRootEl(), {
frameParams: {
width: '100%',
height: '100%',
},
});
await appEmbed.render();
const url = getIFrameSrc();
expect(url).not.toContain('overrideHistoryState');
});

it('Should not add contextMenuEnabledOnWhichClick flag to the iframe src when it is not passed', async () => {
const liveboardEmbed = new LiveboardEmbed(getRootEl(), {
...defaultViewConfig,
Expand Down
4 changes: 4 additions & 0 deletions src/embed/ts-embed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,7 @@ export class TsEmbed {
insertInToSlide,
disableRedirectionLinksInNewTab,
overrideOrgId,
overrideHistoryState,
exposeTranslationIDs,
primaryAction,
} = this.viewConfig;
Expand Down Expand Up @@ -856,6 +857,9 @@ export class TsEmbed {
if (overrideOrgId !== undefined) {
queryParams[Param.OverrideOrgId] = overrideOrgId;
}
if (overrideHistoryState !== undefined) {
queryParams[Param.OverrideHistoryState] = overrideHistoryState;
}

if (this.isPreAuthCacheEnabled()) {
queryParams[Param.preAuthCache] = true;
Expand Down
21 changes: 21 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1179,6 +1179,26 @@ export interface BaseViewConfig extends ApiInterceptFlags {
* ```
*/
overrideOrgId?: number;
/**
* Overrides the browser history behavior for embedding application users.
* This parameter changes standard history navigation (pushState) into a
* state replacement (replaceState), preventing users from getting trapped in
* back-button loops inside the embedded iframe environment.
* The overrideHistoryState setting is honored only if the
* application is running within an embedded context.
*
* Supported embed types: `AppEmbed`, `LiveboardEmbed`, `SearchEmbed`, `SpotterAgentEmbed`, `SpotterEmbed`, `SearchBarEmbed`
* @version SDK: 1.51.0 | ThoughtSpot Cloud: 26.8.0.cl
* @example
* ```js
* // Replace <EmbedComponent> with embed component name. For example, AppEmbed, SearchEmbed, or LiveboardEmbed
* const embed = new <EmbedComponent>('#tsEmbed', {
* // ... other embed view config
* overrideHistoryState: true,
* });
* ```
Comment thread
shivam-kumar-ts marked this conversation as resolved.
*/
overrideHistoryState?: boolean;
/**
* Flag to override the *Open Link in New Tab* context
* menu option.
Expand Down Expand Up @@ -6111,6 +6131,7 @@ export enum Param {
SpotterEnabled = 'isSpotterExperienceEnabled',
IsUnifiedSearchExperienceEnabled = 'isUnifiedSearchExperienceEnabled',
OverrideOrgId = 'orgId',
OverrideHistoryState = 'overrideHistoryState',
OauthPollingInterval = 'oAuthPollingInterval',
IsForceRedirect = 'isForceRedirect',
DataSourceId = 'dataSourceId',
Expand Down
Loading