diff --git a/src/configManager/__test__/configManager.test.ts b/src/configManager/__test__/configManager.test.ts index 2781e675..8d28818b 100644 --- a/src/configManager/__test__/configManager.test.ts +++ b/src/configManager/__test__/configManager.test.ts @@ -1,5 +1,5 @@ import Config, { updateConfigFromUrl } from "../configManager"; -import { getDefaultConfig } from "../config.default"; +import { getDefaultConfig, getUserInitData } from "../config.default"; import { DeepSignal } from "deepsignal"; import { IConfig } from "../../types/types"; @@ -90,6 +90,18 @@ describe("Config", () => { }); }); +describe("config default flags", () => { + test("enableLivePreviewOutsideIframe defaults to false in getDefaultConfig", () => { + const defaultConfig = getDefaultConfig(); + expect(defaultConfig.enableLivePreviewOutsideIframe).toBe(false); + }); + + test("enableLivePreviewOutsideIframe defaults to false in getUserInitData", () => { + const initData = getUserInitData(); + expect(initData.enableLivePreviewOutsideIframe).toBe(false); + }); +}); + describe("update config from url", () => { let config: DeepSignal; diff --git a/src/configManager/__test__/handleUserConfig.test.ts b/src/configManager/__test__/handleUserConfig.test.ts index e668d6fb..5833cc04 100644 --- a/src/configManager/__test__/handleUserConfig.test.ts +++ b/src/configManager/__test__/handleUserConfig.test.ts @@ -411,6 +411,41 @@ describe("handleInitData()", () => { }); }); +describe("handleInitData() - enableLivePreviewOutsideIframe", () => { + let config: DeepSignal; + + beforeEach(() => { + Config.reset(); + config = Config.get(); + }); + + afterAll(() => { + Config.reset(); + }); + + test("should default to false when not provided", () => { + const initData: Partial = {}; + handleInitData(initData); + expect(config.enableLivePreviewOutsideIframe).toBe(false); + }); + + test("should set to true when provided as true", () => { + const initData: Partial = { + enableLivePreviewOutsideIframe: true, + }; + handleInitData(initData); + expect(config.enableLivePreviewOutsideIframe).toBe(true); + }); + + test("should remain false when provided as false", () => { + const initData: Partial = { + enableLivePreviewOutsideIframe: false, + }; + handleInitData(initData); + expect(config.enableLivePreviewOutsideIframe).toBe(false); + }); +}); + describe("handleClientUrlParams()", () => { let config: DeepSignal; diff --git a/src/configManager/config.default.ts b/src/configManager/config.default.ts index 1369c0f3..8a20a7be 100644 --- a/src/configManager/config.default.ts +++ b/src/configManager/config.default.ts @@ -39,6 +39,7 @@ export function getUserInitData(): IInitData { environment: "", }, runScriptsOnUpdate: false, + enableLivePreviewOutsideIframe: false, }; } @@ -110,5 +111,6 @@ export function getDefaultConfig(): IConfig { }, payload: [], }, + enableLivePreviewOutsideIframe: false, }; } diff --git a/src/configManager/handleUserConfig.ts b/src/configManager/handleUserConfig.ts index 4a068ea8..a964e773 100644 --- a/src/configManager/handleUserConfig.ts +++ b/src/configManager/handleUserConfig.ts @@ -121,6 +121,8 @@ export const handleInitData = (initData: Partial): void => { "bottom-right", }) + Config.set("enableLivePreviewOutsideIframe", initData.enableLivePreviewOutsideIframe ?? config.enableLivePreviewOutsideIframe ?? false); + // client URL params handleClientUrlParams( initData.clientUrlParams ?? diff --git a/src/livePreview/eventManager/__test__/postMessageEvent.hooks.test.ts b/src/livePreview/eventManager/__test__/postMessageEvent.hooks.test.ts index 18f9dd70..e9f08798 100644 --- a/src/livePreview/eventManager/__test__/postMessageEvent.hooks.test.ts +++ b/src/livePreview/eventManager/__test__/postMessageEvent.hooks.test.ts @@ -15,6 +15,7 @@ import { import { useOnEntryUpdatePostMessageEvent, useHistoryPostMessageEvent, + sendInitializeLivePreviewPostMessageEvent, } from "../postMessageEvent.hooks"; import { isOpeningInNewTab } from "../../../common/inIframe"; @@ -22,6 +23,7 @@ import { isOpeningInNewTab } from "../../../common/inIframe"; vi.mock("../../../configManager/configManager", () => ({ default: { get: vi.fn(), + set: vi.fn(), }, setConfigFromParams: vi.fn(), })); @@ -35,6 +37,7 @@ vi.mock("../../../logger/logger", () => ({ vi.mock("../livePreviewEventManager", () => ({ default: { on: vi.fn(), + send: vi.fn(), }, })); @@ -42,6 +45,11 @@ vi.mock("../../../common/inIframe", () => ({ isOpeningInNewTab: vi.fn(), })); +vi.mock("../../../utils", () => ({ + addParamsToUrl: vi.fn(), + isOpeningInTimeline: vi.fn(() => false), +})); + describe("postMessageEvent.hooks", () => { let mockWindow: any; let mockConfig: any; @@ -415,4 +423,56 @@ describe("postMessageEvent.hooks", () => { }).toThrow("Unhandled event: unknown"); }); }); + + describe("sendInitializeLivePreviewPostMessageEvent", () => { + beforeEach(() => { + // default send resolves with preview windowType, no contentType/entry to avoid side-effects + (livePreviewPostMessage as any).send.mockResolvedValue({ + windowType: "preview", + }); + }); + + it("should include enableLivePreviewOutsideIframe=false in INIT payload", async () => { + mockConfig = { + ssr: true, // avoid timers + mode: 1, + enableLivePreviewOutsideIframe: false, + }; + (Config.get as any).mockReturnValue(mockConfig); + + await sendInitializeLivePreviewPostMessageEvent(); + // allow microtasks to flush + await Promise.resolve(); + + expect(livePreviewPostMessage?.send).toHaveBeenCalledWith( + LIVE_PREVIEW_POST_MESSAGE_EVENTS.INIT, + expect.objectContaining({ + config: expect.objectContaining({ + enableLivePreviewOutsideIframe: false, + }), + }) + ); + }); + + it("should include enableLivePreviewOutsideIframe=true in INIT payload", async () => { + mockConfig = { + ssr: true, // avoid timers + mode: 1, + enableLivePreviewOutsideIframe: true, + }; + (Config.get as any).mockReturnValue(mockConfig); + + await sendInitializeLivePreviewPostMessageEvent(); + await Promise.resolve(); + + expect(livePreviewPostMessage?.send).toHaveBeenCalledWith( + LIVE_PREVIEW_POST_MESSAGE_EVENTS.INIT, + expect.objectContaining({ + config: expect.objectContaining({ + enableLivePreviewOutsideIframe: true, + }), + }) + ); + }); + }); }); \ No newline at end of file diff --git a/src/livePreview/eventManager/postMessageEvent.hooks.ts b/src/livePreview/eventManager/postMessageEvent.hooks.ts index ac2bbd40..1ef706d3 100644 --- a/src/livePreview/eventManager/postMessageEvent.hooks.ts +++ b/src/livePreview/eventManager/postMessageEvent.hooks.ts @@ -128,6 +128,7 @@ export function sendInitializeLivePreviewPostMessageEvent(): void { href: window.location.href, sdkVersion: process?.env?.PACKAGE_VERSION, mode: Config.get().mode, + enableLivePreviewOutsideIframe: Config.get().enableLivePreviewOutsideIframe, }, } ) diff --git a/src/types/types.ts b/src/types/types.ts index 01d9e174..76986262 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -87,6 +87,7 @@ export declare interface IConfig { highlightedElement: HTMLElement | null; }; collab: ICollabConfig["collab"]; + enableLivePreviewOutsideIframe: boolean; } @@ -132,6 +133,7 @@ export declare interface IInitData { editButton: IConfigEditButton; editInVisualBuilderButton: IConfigEditInVisualBuilderButton; mode: ILivePreviewMode; + enableLivePreviewOutsideIframe: boolean; // default: false } // type PickPartial = Partial> & Omit;