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
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ describe("cslp tooltip", () => {
singularEditButton?.click();

const expectedRedirectUrl =
"https://app.contentstack.com/#!/stack/sample-api-key/content-type/content-type-1/en-us/entry/entry-uid-1/edit?branch=main&preview-field=field-title&preview-locale=en-us&preview-environment=sample-environment";
"https://app.contentstack.com/#!/stack/sample-api-key/content-type/content-type-1/en-us/entry/entry-uid-1/edit?branch=main&preview-field=field-title&preview-locale=en-us&preview-environment=sample-environment&preview-url=http%3A%2F%2Flocalhost%3A3000%2F";

expect(window.open).toHaveBeenCalledWith(expectedRedirectUrl, "_blank");

Expand Down Expand Up @@ -241,7 +241,7 @@ describe("cslp tooltip", () => {
singularEditButton?.click();

const expectedRedirectUrl =
"https://app.contentstack.com/#!/stack/sample-api-key/content-type/content-type-1/en-us/entry/entry-uid-1/edit?branch=dev&preview-field=field-title&preview-locale=en-us&preview-environment=sample-environment";
"https://app.contentstack.com/#!/stack/sample-api-key/content-type/content-type-1/en-us/entry/entry-uid-1/edit?branch=dev&preview-field=field-title&preview-locale=en-us&preview-environment=sample-environment&preview-url=http%3A%2F%2Flocalhost%3A3000%2F";

expect(window.open).toHaveBeenCalledWith(expectedRedirectUrl, "_blank");

Expand Down Expand Up @@ -283,7 +283,7 @@ describe("cslp tooltip", () => {
singularEditButton?.click();

const expectedRedirectUrl =
"https://app.contentstack.com/#!/stack/sample-api-key/content-type/content-type-1/en-us/entry/entry-uid-1/variant/variant-uid-1/edit?branch=dev&preview-field=field-title&preview-locale=en-us&preview-environment=sample-environment";
"https://app.contentstack.com/#!/stack/sample-api-key/content-type/content-type-1/en-us/entry/entry-uid-1/variant/variant-uid-1/edit?branch=dev&preview-field=field-title&preview-locale=en-us&preview-environment=sample-environment&preview-url=http%3A%2F%2Flocalhost%3A3000%2F";

expect(window.open).toHaveBeenCalledWith(expectedRedirectUrl, "_blank");

Expand Down
10 changes: 10 additions & 0 deletions src/livePreview/editButton/editButton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import livePreviewPostMessage from "../eventManager/livePreviewEventManager";
import { EDIT_BUTTON_TOOLTIP_ID } from "./editButton.constant";
import { isOpeningInTimeline } from "../../utils";
import { getCurrentPageUrl } from "../../utils/getCurrentPageUrl";

function calculateEditButtonPosition(
currentHoveredElement: HTMLElement,
Expand Down Expand Up @@ -556,6 +557,15 @@ export class LivePreviewEditButton {
url.searchParams.append("preview-locale", locale ?? "en-us");
url.searchParams.append("preview-environment", environment);

// The page the editor was on. A referenced entry can be rendered on more
// than one page, and a nested one has no page among its direct
// references at all, so the CMS cannot work this out from the entry
// alone — without it the preview falls back to the base URL.
const pageUrl = getCurrentPageUrl();
if (pageUrl) {
url.searchParams.append("preview-url", pageUrl);
}

return `${url.origin}/${url.hash}${url.search}`;
}

Expand Down
38 changes: 38 additions & 0 deletions src/utils/__test__/getCurrentPageUrl.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { getCurrentPageUrl } from "../getCurrentPageUrl";

describe("getCurrentPageUrl", () => {
const setHref = (href: string) => {
Object.defineProperty(window, "location", {
value: new URL(href),
writable: true,
});
};

it("should return the page URL as-is when there are no live preview params", () => {
setHref("https://example.com/products/shoes");

expect(getCurrentPageUrl()).toBe("https://example.com/products/shoes");
});

it("should drop live preview's own query params", () => {
setHref(
"https://example.com/page?live_preview=abc&content_type_uid=hero&entry_uid=blt1&preview_timestamp=123&cslp-buttons=true"
);

expect(getCurrentPageUrl()).toBe("https://example.com/page");
});

it("should keep the site's own query params", () => {
setHref("https://example.com/search?q=fountains&live_preview=abc");

expect(getCurrentPageUrl()).toBe(
"https://example.com/search?q=fountains"
);
});

it("should keep the path that distinguishes one page from another", () => {
setHref("https://example.com/vp1995-riverside-gardens");

expect(getCurrentPageUrl()).toContain("/vp1995-riverside-gardens");
});
});
25 changes: 8 additions & 17 deletions src/utils/addLivePreviewQueryTags.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,18 @@
import { PublicLogger } from "../logger/logger";
import { LIVE_PREVIEW_QUERY_PARAMS } from "./livePreviewQueryParams.constant";
export function addLivePreviewQueryTags(link: string): string {
try {
const docUrl: URL = new URL(document.location.href);
const newUrl: URL = new URL(link);
const livePreviewHash: string | null =
docUrl.searchParams.get("live_preview");
const ctUid: string | null =
docUrl.searchParams.get("content_type_uid");
const entryUid: string | null = docUrl.searchParams.get("entry_uid");
const previewTimestamp: string | null = docUrl.searchParams.get("preview_timestamp");
if (livePreviewHash) {
newUrl.searchParams.set("live_preview", livePreviewHash);
}
if(ctUid && entryUid){
newUrl.searchParams.set("content_type_uid", ctUid);
newUrl.searchParams.set("entry_uid", entryUid);
}
if (previewTimestamp) {
newUrl.searchParams.set("preview_timestamp", previewTimestamp);
}
LIVE_PREVIEW_QUERY_PARAMS.forEach((param) => {
const value: string | null = docUrl.searchParams.get(param);
if (value) {
newUrl.searchParams.set(param, value);
}
});
return newUrl.href;
} catch (error) {
PublicLogger.error("Error while adding live preview to URL");
return link;
}
}
}
35 changes: 35 additions & 0 deletions src/utils/getCurrentPageUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { PublicLogger } from "../logger/logger";
import { LIVE_PREVIEW_QUERY_PARAMS } from "./livePreviewQueryParams.constant";

/**
* The preview session parameters plus `cslp-buttons`, which the edit button reads
* off the page URL. All of them describe the preview, not the page, so they are
* dropped before the URL is handed back to the CMS.
*/
const PARAMS_TO_DROP = [...LIVE_PREVIEW_QUERY_PARAMS, "cslp-buttons"];

/**
* The URL of the page the visitor is on, without live preview's own query
* parameters.
*
* The CMS uses this to keep the preview on the page the editor clicked Edit
* from. It cannot derive that from the entry: a referenced entry can appear on
* several pages, and a nested one (page -> hero -> image) has no page among its
* direct references at all.
*
* Returns an empty string outside a browser or if the URL cannot be parsed, so
* callers can simply omit the parameter.
*/
export function getCurrentPageUrl(): string {
try {
if (typeof window === "undefined" || !window.location?.href) return "";

const url = new URL(window.location.href);
PARAMS_TO_DROP.forEach((param) => url.searchParams.delete(param));

return url.href;
} catch (error) {
PublicLogger.error("Error while reading the current page URL");
return "";
}
}
14 changes: 14 additions & 0 deletions src/utils/livePreviewQueryParams.constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Query parameters live preview adds to the page URL. They describe the preview
* session, not the page.
*
* Single source for the two inverse operations on them: `addLivePreviewQueryTags`
* carries them onto internal links, `getCurrentPageUrl` drops them before the URL
* goes back to the CMS. Add a parameter here and both sides pick it up.
*/
export const LIVE_PREVIEW_QUERY_PARAMS = [
"live_preview",
"content_type_uid",
"entry_uid",
"preview_timestamp",
];
Loading