Skip to content
Merged
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
8 changes: 8 additions & 0 deletions src/common/inIframe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ export function inIframe(): boolean {
}
}

export function inVisualEditor(): boolean{
try {
return inIframe() && window?.name == 'visual-editor'
} catch (e) {
return false;
}
}

export function isOpeningInNewTab(): boolean {
try {
if(hasWindow()) {
Expand Down
69 changes: 68 additions & 1 deletion src/configManager/__test__/configManager.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Config, { updateConfigFromUrl } from "../configManager";
import Config, { updateConfigFromUrl, syncToStackSdk } from "../configManager";
import { getDefaultConfig, getUserInitData } from "../config.default";
import { DeepSignal } from "deepsignal";
import { IConfig } from "../../types/types";
Expand Down Expand Up @@ -102,6 +102,73 @@ describe("config default flags", () => {
});
});

describe("syncToStackSdk", () => {
beforeEach(() => {
Config.reset();
});

afterAll(() => {
Config.reset();
});

test("should set hash, stackSdkLivePreview.hash and stackSdkLivePreview.live_preview when hash is provided", () => {
syncToStackSdk({ hash: "abc123" });

const config = Config.get();
expect(config.stackSdk.live_preview.hash).toBe("abc123");
expect(config.stackSdk.live_preview.live_preview).toBe("abc123");
expect(config.stackSdk.live_preview.content_type_uid).toBeUndefined();
expect(config.stackSdk.live_preview.entry_uid).toBeUndefined();
});

test("should set content_type_uid on stackSdk when contentTypeUid is provided", () => {
syncToStackSdk({ contentTypeUid: "blog" });

const config = Config.get();
expect(config.stackSdk.live_preview.content_type_uid).toBe("blog");
expect(config.stackSdk.live_preview.hash).toBeUndefined();
expect(config.stackSdk.live_preview.entry_uid).toBeUndefined();
});

test("should set entry_uid on stackSdk when entryUid is provided", () => {
syncToStackSdk({ entryUid: "entry-42" });

const config = Config.get();
expect(config.stackSdk.live_preview.entry_uid).toBe("entry-42");
expect(config.stackSdk.live_preview.hash).toBeUndefined();
expect(config.stackSdk.live_preview.content_type_uid).toBeUndefined();
});

test("should set all three fields when all params are provided", () => {
syncToStackSdk({ hash: "h1", contentTypeUid: "page", entryUid: "e1" });

const config = Config.get();
expect(config.stackSdk.live_preview.hash).toBe("h1");
expect(config.stackSdk.live_preview.live_preview).toBe("h1");
expect(config.stackSdk.live_preview.content_type_uid).toBe("page");
expect(config.stackSdk.live_preview.entry_uid).toBe("e1");
});

test("should skip falsy values — null and undefined are ignored", () => {
syncToStackSdk({ hash: null, contentTypeUid: undefined, entryUid: null });

const config = Config.get();
expect(config.stackSdk.live_preview.hash).toBeUndefined();
expect(config.stackSdk.live_preview.content_type_uid).toBeUndefined();
expect(config.stackSdk.live_preview.entry_uid).toBeUndefined();
});

test("should not overwrite existing stackSdk values for keys not passed", () => {
syncToStackSdk({ hash: "first", contentTypeUid: "ct1", entryUid: "e1" });
syncToStackSdk({ hash: "second" });

const config = Config.get();
expect(config.stackSdk.live_preview.hash).toBe("second");
expect(config.stackSdk.live_preview.content_type_uid).toBe("ct1");
expect(config.stackSdk.live_preview.entry_uid).toBe("e1");
});
});

describe("update config from url", () => {
let config: DeepSignal<IConfig>;

Expand Down
40 changes: 34 additions & 6 deletions src/configManager/configManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,50 @@ export function setConfigFromParams(
const content_type_uid = urlParams.get("content_type_uid");
const entry_uid = urlParams.get("entry_uid");

const stackSdkLivePreview = Config.get().stackSdk.live_preview;

if (live_preview) {
Config.set("hash", live_preview);
stackSdkLivePreview.hash = live_preview;
stackSdkLivePreview.live_preview = live_preview;
}

if (content_type_uid) {
Config.set("stackDetails.contentTypeUid", content_type_uid);
stackSdkLivePreview.content_type_uid = content_type_uid;
}

if (entry_uid) {
Config.set("stackDetails.entryUid", entry_uid);
stackSdkLivePreview.entry_uid = entry_uid;
}

syncToStackSdk({
hash: live_preview,
contentTypeUid: content_type_uid,
entryUid: entry_uid,
});
}

/**
* Syncs hash, contentTypeUid, and entryUid into the user's stackSdk.live_preview object.
* Auto-effects via deepsignal were ruled out because Config.reset() replaces the deepSignal
* instance, which would blind any bound effect. Explicit sync is the safe alternative.
*/
export function syncToStackSdk({
hash,
contentTypeUid,
entryUid,
}: {
hash?: string | null;
contentTypeUid?: string | null;
entryUid?: string | null;
}): void {
const stackSdkLivePreview = Config.get().stackSdk.live_preview;

if (hash) {
stackSdkLivePreview.hash = hash;
stackSdkLivePreview.live_preview = hash;
}
if (contentTypeUid) {
stackSdkLivePreview.content_type_uid = contentTypeUid;
}
if (entryUid) {
stackSdkLivePreview.entry_uid = entryUid;
}

Config.set("stackSdk.live_preview", stackSdkLivePreview);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/

import { vi } from "vitest";
import Config, { setConfigFromParams } from "../../../configManager/configManager";
import Config, { syncToStackSdk } from "../../../configManager/configManager";
import { PublicLogger } from "../../../logger/logger";
import livePreviewPostMessage from "../livePreviewEventManager";
import { LIVE_PREVIEW_POST_MESSAGE_EVENTS } from "../livePreviewEventManager.constant";
Expand All @@ -26,7 +26,7 @@ vi.mock("../../../configManager/configManager", () => ({
get: vi.fn(),
set: vi.fn(),
},
setConfigFromParams: vi.fn(),
syncToStackSdk: vi.fn(),
}));

vi.mock("../../../logger/logger", () => ({
Expand All @@ -44,6 +44,7 @@ vi.mock("../livePreviewEventManager", () => ({

vi.mock("../../../common/inIframe", () => ({
isOpeningInNewTab: vi.fn(),
inVisualEditor: vi.fn(() => false),
}));

vi.mock("../../../utils", () => ({
Expand Down Expand Up @@ -138,9 +139,8 @@ describe("postMessageEvent.hooks", () => {
const callback = mockWindow._eventCallbacks[LIVE_PREVIEW_POST_MESSAGE_EVENTS.ON_CHANGE];
callback({ data: eventData });

expect(setConfigFromParams).toHaveBeenCalledWith({
live_preview: "test-hash",
});
expect(Config.set).toHaveBeenCalledWith("hash", "test-hash");
expect(syncToStackSdk).toHaveBeenCalledWith({ hash: "test-hash" });
expect(mockOnChange).toHaveBeenCalled();
});

Expand All @@ -155,9 +155,8 @@ describe("postMessageEvent.hooks", () => {
const callback = mockWindow._eventCallbacks[LIVE_PREVIEW_POST_MESSAGE_EVENTS.ON_CHANGE];
callback({ data: eventData });

expect(setConfigFromParams).toHaveBeenCalledWith({
live_preview: "test-hash",
});
expect(Config.set).toHaveBeenCalledWith("hash", "test-hash");
expect(syncToStackSdk).toHaveBeenCalledWith({ hash: "test-hash" });
expect(mockOnChange).not.toHaveBeenCalled();
});
});
Expand All @@ -179,9 +178,8 @@ describe("postMessageEvent.hooks", () => {
const callback = mockWindow._eventCallbacks[LIVE_PREVIEW_POST_MESSAGE_EVENTS.ON_CHANGE];
callback({ data: eventData });

expect(setConfigFromParams).toHaveBeenCalledWith({
live_preview: "test-hash",
});
expect(Config.set).toHaveBeenCalledWith("hash", "test-hash");
expect(syncToStackSdk).toHaveBeenCalledWith({ hash: "test-hash" });
expect(mockWindow.location.reload).toHaveBeenCalled();
expect(mockOnChange).not.toHaveBeenCalled();
});
Expand All @@ -197,9 +195,8 @@ describe("postMessageEvent.hooks", () => {
const callback = mockWindow._eventCallbacks[LIVE_PREVIEW_POST_MESSAGE_EVENTS.ON_CHANGE];
callback({ data: eventData });

expect(setConfigFromParams).toHaveBeenCalledWith({
live_preview: "test-hash",
});
expect(Config.set).toHaveBeenCalledWith("hash", "test-hash");
expect(syncToStackSdk).toHaveBeenCalledWith({ hash: "test-hash" });
expect(mockWindow.location.reload).not.toHaveBeenCalled();
expect(mockOnChange).not.toHaveBeenCalled();
});
Expand Down Expand Up @@ -307,9 +304,8 @@ describe("postMessageEvent.hooks", () => {
const callback = mockWindow._eventCallbacks[LIVE_PREVIEW_POST_MESSAGE_EVENTS.ON_CHANGE];
callback({ data: eventData });

expect(setConfigFromParams).toHaveBeenCalledWith({
live_preview: "new-hash-value",
});
expect(Config.set).toHaveBeenCalledWith("hash", "new-hash-value");
expect(syncToStackSdk).toHaveBeenCalledWith({ hash: "new-hash-value" });
expect(mockWindow.history.pushState).toHaveBeenCalledWith(
{},
"",
Expand All @@ -330,9 +326,8 @@ describe("postMessageEvent.hooks", () => {
const callback = mockWindow._eventCallbacks[LIVE_PREVIEW_POST_MESSAGE_EVENTS.ON_CHANGE];
callback({ data: eventData });

expect(setConfigFromParams).toHaveBeenCalledWith({
live_preview: "updated-hash",
});
expect(Config.set).toHaveBeenCalledWith("hash", "updated-hash");
expect(syncToStackSdk).toHaveBeenCalledWith({ hash: "updated-hash" });
expect(mockWindow.history.pushState).toHaveBeenCalledWith(
{},
"",
Expand Down Expand Up @@ -360,9 +355,8 @@ describe("postMessageEvent.hooks", () => {
const callback = mockWindow._eventCallbacks[LIVE_PREVIEW_POST_MESSAGE_EVENTS.ON_CHANGE];
callback({ data: eventData });

expect(setConfigFromParams).toHaveBeenCalledWith({
live_preview: "test-hash",
});
expect(Config.set).toHaveBeenCalledWith("hash", "test-hash");
expect(syncToStackSdk).toHaveBeenCalledWith({ hash: "test-hash" });
expect(mockWindow.location.href).toBe("https://newdomain.com/new-page");
});

Expand All @@ -378,9 +372,8 @@ describe("postMessageEvent.hooks", () => {
const callback = mockWindow._eventCallbacks[LIVE_PREVIEW_POST_MESSAGE_EVENTS.ON_CHANGE];
callback({ data: eventData });

expect(setConfigFromParams).toHaveBeenCalledWith({
live_preview: "test-hash",
});
expect(Config.set).toHaveBeenCalledWith("hash", "test-hash");
expect(syncToStackSdk).toHaveBeenCalledWith({ hash: "test-hash" });
expect(mockWindow.location.href).toBe(originalHref);
});
});
Expand Down Expand Up @@ -431,9 +424,9 @@ describe("postMessageEvent.hooks", () => {
);
});

it("should handle errors when setConfigFromParams throws", () => {
(setConfigFromParams as any).mockImplementation(() => {
throw new Error("setConfigFromParams error");
it("should handle errors when syncToStackSdk throws", () => {
(syncToStackSdk as any).mockImplementation(() => {
throw new Error("syncToStackSdk error");
});

const eventData: OnChangeLivePreviewPostMessageEventData = {
Expand Down Expand Up @@ -463,7 +456,8 @@ describe("postMessageEvent.hooks", () => {
const callback = mockWindow._eventCallbacks[LIVE_PREVIEW_POST_MESSAGE_EVENTS.ON_CHANGE];
callback({ data: eventData });

expect(setConfigFromParams).toHaveBeenCalledWith({ live_preview: "test-hash" });
expect(Config.set).toHaveBeenCalledWith("hash", "test-hash");
expect(syncToStackSdk).toHaveBeenCalledWith({ hash: "test-hash" });
expect(mockWindow.location.reload).not.toHaveBeenCalled();
expect(mockOnChange).not.toHaveBeenCalled();
});
Expand All @@ -483,7 +477,8 @@ describe("postMessageEvent.hooks", () => {
const callback = mockWindow._eventCallbacks[LIVE_PREVIEW_POST_MESSAGE_EVENTS.ON_CHANGE];
callback({ data: eventData });

expect(setConfigFromParams).toHaveBeenCalledWith({ live_preview: "new-hash" });
expect(Config.set).toHaveBeenCalledWith("hash", "new-hash");
expect(syncToStackSdk).toHaveBeenCalledWith({ hash: "new-hash" });
expect(mockWindow.history.pushState).not.toHaveBeenCalled();
});

Expand All @@ -504,7 +499,8 @@ describe("postMessageEvent.hooks", () => {
const callback = mockWindow._eventCallbacks[LIVE_PREVIEW_POST_MESSAGE_EVENTS.ON_CHANGE];
callback({ data: eventData });

expect(setConfigFromParams).toHaveBeenCalledWith({ live_preview: "test-hash" });
expect(Config.set).toHaveBeenCalledWith("hash", "test-hash");
expect(syncToStackSdk).toHaveBeenCalledWith({ hash: "test-hash" });
expect(mockWindow.location.href).toBe(originalHref);
});
});
Expand Down Expand Up @@ -636,7 +632,7 @@ describe("postMessageEvent.hooks", () => {
);
});

it("should call setConfigFromParams with content_type_uid and entry_uid when INIT response provides them", async () => {
it("should sync contentTypeUid and entryUid to Config and stackSdk when INIT response provides them", async () => {
mockConfig = {
ssr: true,
mode: 1,
Expand All @@ -651,10 +647,9 @@ describe("postMessageEvent.hooks", () => {
await sendInitializeLivePreviewPostMessageEvent();
await Promise.resolve();

expect(setConfigFromParams).toHaveBeenCalledWith({
content_type_uid: "blog",
entry_uid: "entry-123",
});
expect(Config.set).toHaveBeenCalledWith("stackDetails.contentTypeUid", "blog");
expect(Config.set).toHaveBeenCalledWith("stackDetails.entryUid", "entry-123");
expect(syncToStackSdk).toHaveBeenCalledWith({ contentTypeUid: "blog", entryUid: "entry-123" });
});

it("should return early and skip post-init setup when windowType is BUILDER", async () => {
Expand All @@ -673,8 +668,9 @@ describe("postMessageEvent.hooks", () => {
await sendInitializeLivePreviewPostMessageEvent();
await Promise.resolve();

expect(setConfigFromParams).not.toHaveBeenCalled();
expect(Config.set).not.toHaveBeenCalled();
expect(syncToStackSdk).not.toHaveBeenCalled();
expect(Config.set).not.toHaveBeenCalledWith("stackDetails.contentTypeUid", expect.anything());
expect(Config.set).not.toHaveBeenCalledWith("stackDetails.entryUid", expect.anything());
});

it("should start CHECK_ENTRY_PAGE interval when ssr is false", async () => {
Expand Down
Loading
Loading