From 6994d66ec95157a8aa38ecb7270cffdda4fdf6f3 Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Wed, 4 Dec 2024 11:20:46 -0500 Subject: [PATCH 1/4] fixes test app --- .../app/live-preview/(pages)/[slug]/page.tsx | 4 +++- .../live-preview/app/live-preview/_api/getDoc.ts | 2 +- .../live-preview/_components/Footer/index.tsx | 4 ++-- .../app/live-preview/_components/Link/index.tsx | 16 +++++++++------- .../live-preview/_components/Footer/index.tsx | 4 ++-- test/live-preview/seed/posts-page.ts | 2 +- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/test/live-preview/app/live-preview/(pages)/[slug]/page.tsx b/test/live-preview/app/live-preview/(pages)/[slug]/page.tsx index c127ae89ae9..ceb912059d0 100644 --- a/test/live-preview/app/live-preview/(pages)/[slug]/page.tsx +++ b/test/live-preview/app/live-preview/(pages)/[slug]/page.tsx @@ -1,3 +1,4 @@ +/* eslint-disable no-restricted-exports */ import { notFound } from 'next/navigation.js' import React from 'react' @@ -32,10 +33,11 @@ export default async function Page({ params: paramsPromise }: Args) { export async function generateStaticParams() { process.env.PAYLOAD_DROP_DATABASE = 'false' + try { const pages = await getDocs('pages') return pages?.map(({ slug }) => slug) - } catch (error) { + } catch (_err) { return [] } } diff --git a/test/live-preview/app/live-preview/_api/getDoc.ts b/test/live-preview/app/live-preview/_api/getDoc.ts index bc2a05441ab..c0bdc8a3082 100644 --- a/test/live-preview/app/live-preview/_api/getDoc.ts +++ b/test/live-preview/app/live-preview/_api/getDoc.ts @@ -35,5 +35,5 @@ export const getDoc = async (args: { console.log('Error getting doc', err) } - throw new Error('Error getting doc') + throw new Error('No doc found') } diff --git a/test/live-preview/app/live-preview/_components/Footer/index.tsx b/test/live-preview/app/live-preview/_components/Footer/index.tsx index 32a96b24320..426c049f66a 100644 --- a/test/live-preview/app/live-preview/_components/Footer/index.tsx +++ b/test/live-preview/app/live-preview/_components/Footer/index.tsx @@ -21,7 +21,7 @@ export async function Footer() { Payload Logo @@ -30,7 +30,7 @@ export async function Footer() { return })} Admin - + Source Code Payload diff --git a/test/live-preview/app/live-preview/_components/Link/index.tsx b/test/live-preview/app/live-preview/_components/Link/index.tsx index a940a83e4cb..d5d273133e2 100644 --- a/test/live-preview/app/live-preview/_components/Link/index.tsx +++ b/test/live-preview/app/live-preview/_components/Link/index.tsx @@ -1,4 +1,4 @@ -import LinkWithDefault from 'next/link.js' +import NextLinkImport from 'next/link.js' import React from 'react' import type { Page, Post } from '../../../../payload-types.js' @@ -6,7 +6,7 @@ import type { Props as ButtonProps } from '../Button/index.js' import { Button } from '../Button/index.js' -const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default +const NextLink = (NextLinkImport.default || NextLinkImport) as typeof NextLinkImport.default type CMSLinkType = { appearance?: ButtonProps['appearance'] @@ -36,20 +36,22 @@ export const CMSLink: React.FC = ({ }) => { const href = type === 'reference' && typeof reference?.value === 'object' && reference.value.slug - ? `/${reference.value.slug}` + ? `/live-preview/${reference.value.slug}` : url - if (!href) return null + if (!href) { + return null + } if (!appearance) { const newTabProps = newTab ? { rel: 'noopener noreferrer', target: '_blank' } : {} if (href || url) { return ( - + {label && label} - {children && children} - + {children || null} + ) } } diff --git a/test/live-preview/prod/app/live-preview/_components/Footer/index.tsx b/test/live-preview/prod/app/live-preview/_components/Footer/index.tsx index 32a96b24320..426c049f66a 100644 --- a/test/live-preview/prod/app/live-preview/_components/Footer/index.tsx +++ b/test/live-preview/prod/app/live-preview/_components/Footer/index.tsx @@ -21,7 +21,7 @@ export async function Footer() { Payload Logo @@ -30,7 +30,7 @@ export async function Footer() { return })} Admin - + Source Code Payload diff --git a/test/live-preview/seed/posts-page.ts b/test/live-preview/seed/posts-page.ts index 998919229ac..7cdf2ab6271 100644 --- a/test/live-preview/seed/posts-page.ts +++ b/test/live-preview/seed/posts-page.ts @@ -4,7 +4,7 @@ import { postsSlug } from '../shared.js' export const postsPage: Partial = { title: 'Posts', - slug: 'live-preview/posts', + slug: 'posts', meta: { title: 'Payload Website Template', description: 'An open-source website built with Payload and Next.js.', From aada4305792bd3b17aeef12e235f868851166ade Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Wed, 4 Dec 2024 11:58:00 -0500 Subject: [PATCH 2/4] fix(live-preview): supports relative urls for dynamic preview deployments --- packages/next/src/views/LivePreview/index.tsx | 7 ++++++- test/live-preview/app/live-preview/_api/getDoc.ts | 2 +- test/live-preview/utilities/formatLivePreviewURL.ts | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/next/src/views/LivePreview/index.tsx b/packages/next/src/views/LivePreview/index.tsx index d33e3fa587c..ec22ccb85e2 100644 --- a/packages/next/src/views/LivePreview/index.tsx +++ b/packages/next/src/views/LivePreview/index.tsx @@ -36,7 +36,7 @@ export const LivePreviewView: PayloadServerReactComponent = a }, ] - const url = + let url = typeof livePreviewConfig?.url === 'function' ? await livePreviewConfig.url({ collectionConfig, @@ -47,5 +47,10 @@ export const LivePreviewView: PayloadServerReactComponent = a }) : livePreviewConfig?.url + // Support relative URLs by prepending the origin, if necessary + if (url && url.startsWith('/')) { + url = `${initPageResult.req.origin}${url}` + } + return } diff --git a/test/live-preview/app/live-preview/_api/getDoc.ts b/test/live-preview/app/live-preview/_api/getDoc.ts index c0bdc8a3082..08e7a206f28 100644 --- a/test/live-preview/app/live-preview/_api/getDoc.ts +++ b/test/live-preview/app/live-preview/_api/getDoc.ts @@ -32,7 +32,7 @@ export const getDoc = async (args: { return docs[0] as T } } catch (err) { - console.log('Error getting doc', err) + throw new Error(`Error getting doc: ${err.message}`) } throw new Error('No doc found') diff --git a/test/live-preview/utilities/formatLivePreviewURL.ts b/test/live-preview/utilities/formatLivePreviewURL.ts index d7ee07b8fe5..0e710f57e04 100644 --- a/test/live-preview/utilities/formatLivePreviewURL.ts +++ b/test/live-preview/utilities/formatLivePreviewURL.ts @@ -5,7 +5,7 @@ export const formatLivePreviewURL: LivePreviewConfig['url'] = async ({ collectionConfig, payload, }) => { - let baseURL = 'http://localhost:3000/live-preview' + let baseURL = '/live-preview' // You can run async requests here, if needed // For example, multi-tenant apps may need to lookup additional data @@ -25,6 +25,7 @@ export const formatLivePreviewURL: LivePreviewConfig['url'] = async ({ .then((res) => res?.docs?.[0]) if (fullTenant?.clientURL) { + // Note: appending a fully-qualified URL here won't work for preview deployments on Vercel baseURL = `${fullTenant.clientURL}/live-preview` } } catch (e) { From 46e64224bc12cd4416590b9cd43b965740fe4c42 Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Wed, 4 Dec 2024 12:50:59 -0500 Subject: [PATCH 3/4] docs and more cleanup to test app --- docs/live-preview/overview.mdx | 2 ++ .../app/live-preview/_api/getDocs.ts | 12 +++++++----- .../app/live-preview/(pages)/[slug]/page.tsx | 4 +++- .../prod/app/live-preview/_api/getDoc.ts | 4 ++-- .../prod/app/live-preview/_api/getDocs.ts | 12 +++++++----- .../app/live-preview/_components/Link/index.tsx | 16 +++++++++------- 6 files changed, 30 insertions(+), 20 deletions(-) diff --git a/docs/live-preview/overview.mdx b/docs/live-preview/overview.mdx index 26991bdf7c4..7900e0db805 100644 --- a/docs/live-preview/overview.mdx +++ b/docs/live-preview/overview.mdx @@ -54,6 +54,8 @@ _\* An asterisk denotes that a property is required._ The `url` property is a string that points to your front-end application. This value is used as the `src` attribute of the iframe rendering your front-end. Once loaded, the Admin Panel will communicate directly with your app through `window.postMessage` events. +This can be an absolute URL or a relative path. If you are using a relative path, Payload will resolve it relative to the application's origin URL. This is useful for Vercel preview deployments, for example, where URLs are not known ahead of time. + To set the URL, use the `admin.livePreview.url` property in your [Payload Config](../configuration/overview): ```ts diff --git a/test/live-preview/app/live-preview/_api/getDocs.ts b/test/live-preview/app/live-preview/_api/getDocs.ts index 4d8fc2a442d..fc2296508eb 100644 --- a/test/live-preview/app/live-preview/_api/getDocs.ts +++ b/test/live-preview/app/live-preview/_api/getDocs.ts @@ -1,7 +1,7 @@ import config from '@payload-config' -import { getPayload } from 'payload' +import { type CollectionSlug, getPayload } from 'payload' -export const getDocs = async (collection: string): Promise => { +export const getDocs = async (collection: CollectionSlug): Promise => { const payload = await getPayload({ config }) try { @@ -11,10 +11,12 @@ export const getDocs = async (collection: string): Promise => { limit: 100, }) - return docs as T[] + if (docs) { + return docs as T[] + } } catch (err) { - console.error(err) + throw new Error(`Error getting docs: ${err.message}`) } - throw new Error('Error getting docs') + throw new Error('No docs found') } diff --git a/test/live-preview/prod/app/live-preview/(pages)/[slug]/page.tsx b/test/live-preview/prod/app/live-preview/(pages)/[slug]/page.tsx index 69d394d4d19..ea055c68e69 100644 --- a/test/live-preview/prod/app/live-preview/(pages)/[slug]/page.tsx +++ b/test/live-preview/prod/app/live-preview/(pages)/[slug]/page.tsx @@ -1,3 +1,4 @@ +/* eslint-disable no-restricted-exports */ import { notFound } from 'next/navigation.js' import React from 'react' @@ -32,10 +33,11 @@ export default async function Page({ params: paramsPromise }: Args) { export async function generateStaticParams() { process.env.PAYLOAD_DROP_DATABASE = 'false' + try { const pages = await getDocs('pages') return pages?.map(({ slug }) => slug) - } catch (error) { + } catch (_err) { return [] } } diff --git a/test/live-preview/prod/app/live-preview/_api/getDoc.ts b/test/live-preview/prod/app/live-preview/_api/getDoc.ts index bc2a05441ab..08e7a206f28 100644 --- a/test/live-preview/prod/app/live-preview/_api/getDoc.ts +++ b/test/live-preview/prod/app/live-preview/_api/getDoc.ts @@ -32,8 +32,8 @@ export const getDoc = async (args: { return docs[0] as T } } catch (err) { - console.log('Error getting doc', err) + throw new Error(`Error getting doc: ${err.message}`) } - throw new Error('Error getting doc') + throw new Error('No doc found') } diff --git a/test/live-preview/prod/app/live-preview/_api/getDocs.ts b/test/live-preview/prod/app/live-preview/_api/getDocs.ts index 4d8fc2a442d..fc2296508eb 100644 --- a/test/live-preview/prod/app/live-preview/_api/getDocs.ts +++ b/test/live-preview/prod/app/live-preview/_api/getDocs.ts @@ -1,7 +1,7 @@ import config from '@payload-config' -import { getPayload } from 'payload' +import { type CollectionSlug, getPayload } from 'payload' -export const getDocs = async (collection: string): Promise => { +export const getDocs = async (collection: CollectionSlug): Promise => { const payload = await getPayload({ config }) try { @@ -11,10 +11,12 @@ export const getDocs = async (collection: string): Promise => { limit: 100, }) - return docs as T[] + if (docs) { + return docs as T[] + } } catch (err) { - console.error(err) + throw new Error(`Error getting docs: ${err.message}`) } - throw new Error('Error getting docs') + throw new Error('No docs found') } diff --git a/test/live-preview/prod/app/live-preview/_components/Link/index.tsx b/test/live-preview/prod/app/live-preview/_components/Link/index.tsx index 25ae59b6739..5b60e5a654d 100644 --- a/test/live-preview/prod/app/live-preview/_components/Link/index.tsx +++ b/test/live-preview/prod/app/live-preview/_components/Link/index.tsx @@ -1,4 +1,4 @@ -import LinkWithDefault from 'next/link.js' +import NextLinkImport from 'next/link.js' import React from 'react' import type { Page, Post } from '../../../../../payload-types.js' @@ -6,7 +6,7 @@ import type { Props as ButtonProps } from '../Button/index.js' import { Button } from '../Button/index.js' -const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default +const NextLink = (NextLinkImport.default || NextLinkImport) as typeof NextLinkImport.default type CMSLinkType = { appearance?: ButtonProps['appearance'] @@ -36,20 +36,22 @@ export const CMSLink: React.FC = ({ }) => { const href = type === 'reference' && typeof reference?.value === 'object' && reference.value.slug - ? `/${reference.value.slug}` + ? `/live-preview/${reference.value.slug}` : url - if (!href) return null + if (!href) { + return null + } if (!appearance) { const newTabProps = newTab ? { rel: 'noopener noreferrer', target: '_blank' } : {} if (href || url) { return ( - + {label && label} - {children && children} - + {children || null} + ) } } From 3ab0d23cb06cea94865fbc5940cc61ed7d8d701f Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Wed, 4 Dec 2024 12:59:17 -0500 Subject: [PATCH 4/4] concats protocol and host instead of domain to support localhost --- packages/next/src/views/LivePreview/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/src/views/LivePreview/index.tsx b/packages/next/src/views/LivePreview/index.tsx index ec22ccb85e2..399cec0e096 100644 --- a/packages/next/src/views/LivePreview/index.tsx +++ b/packages/next/src/views/LivePreview/index.tsx @@ -49,7 +49,7 @@ export const LivePreviewView: PayloadServerReactComponent = a // Support relative URLs by prepending the origin, if necessary if (url && url.startsWith('/')) { - url = `${initPageResult.req.origin}${url}` + url = `${initPageResult.req.protocol}//${initPageResult.req.host}${url}` } return