diff --git a/packages/payload/src/admin/components/views/LivePreview/Context/context.ts b/packages/payload/src/admin/components/views/LivePreview/Context/context.ts index aa874fbc1b..0c6f2a4c45 100644 --- a/packages/payload/src/admin/components/views/LivePreview/Context/context.ts +++ b/packages/payload/src/admin/components/views/LivePreview/Context/context.ts @@ -2,12 +2,12 @@ import type { Dispatch } from 'react' import { createContext, useContext } from 'react' -import type { LivePreview } from '../../../../../exports/config' +import type { LivePreviewConfig } from '../../../../../exports/config' import type { SizeReducerAction } from './sizeReducer' export interface LivePreviewContextType { - breakpoint: LivePreview['breakpoints'][number]['name'] - breakpoints: LivePreview['breakpoints'] + breakpoint: LivePreviewConfig['breakpoints'][number]['name'] + breakpoints: LivePreviewConfig['breakpoints'] deviceFrameRef: React.RefObject iframeHasLoaded: boolean iframeRef: React.RefObject @@ -15,7 +15,7 @@ export interface LivePreviewContextType { height: number width: number } - setBreakpoint: (breakpoint: LivePreview['breakpoints'][number]['name']) => void + setBreakpoint: (breakpoint: LivePreviewConfig['breakpoints'][number]['name']) => void setHeight: (height: number) => void setIframeHasLoaded: (loaded: boolean) => void setSize: Dispatch diff --git a/packages/payload/src/admin/components/views/LivePreview/Context/index.tsx b/packages/payload/src/admin/components/views/LivePreview/Context/index.tsx index 09457fde24..ce98061702 100644 --- a/packages/payload/src/admin/components/views/LivePreview/Context/index.tsx +++ b/packages/payload/src/admin/components/views/LivePreview/Context/index.tsx @@ -1,7 +1,7 @@ import { DndContext } from '@dnd-kit/core' import React, { useCallback, useEffect } from 'react' -import type { LivePreview } from '../../../../../exports/config' +import type { LivePreviewConfig } from '../../../../../exports/config' import type { EditViewProps } from '../../types' import type { usePopupWindow } from '../usePopupWindow' @@ -11,7 +11,7 @@ import { LivePreviewContext } from './context' import { sizeReducer } from './sizeReducer' export type ToolbarProviderProps = EditViewProps & { - breakpoints?: LivePreview['breakpoints'] + breakpoints?: LivePreviewConfig['breakpoints'] children: React.ReactNode deviceSize?: { height: number @@ -37,7 +37,7 @@ export const LivePreviewProvider: React.FC = (props) => { const [size, setSize] = React.useReducer(sizeReducer, { height: 0, width: 0 }) const [breakpoint, setBreakpoint] = - React.useState('responsive') + React.useState('responsive') // The toolbar needs to freely drag and drop around the page const handleDragEnd = (ev) => { diff --git a/packages/payload/src/admin/components/views/LivePreview/Preview/index.tsx b/packages/payload/src/admin/components/views/LivePreview/Preview/index.tsx index 52f3cc26e3..d452ec2edf 100644 --- a/packages/payload/src/admin/components/views/LivePreview/Preview/index.tsx +++ b/packages/payload/src/admin/components/views/LivePreview/Preview/index.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react' -import type { LivePreview as LivePreviewType } from '../../../../../exports/config' +import type { LivePreviewConfig } from '../../../../../exports/config' import type { Field } from '../../../../../fields/config/types' import type { EditViewProps } from '../../types' import type { usePopupWindow } from '../usePopupWindow' @@ -121,7 +121,7 @@ export const LivePreview: React.FC< > = (props) => { let url - let breakpoints: LivePreviewType['breakpoints'] = [ + let breakpoints: LivePreviewConfig['breakpoints'] = [ { name: 'responsive', height: '100%', diff --git a/packages/payload/src/admin/components/views/LivePreview/index.tsx b/packages/payload/src/admin/components/views/LivePreview/index.tsx index 4b528892c8..24621c920d 100644 --- a/packages/payload/src/admin/components/views/LivePreview/index.tsx +++ b/packages/payload/src/admin/components/views/LivePreview/index.tsx @@ -1,6 +1,7 @@ import React, { Fragment } from 'react' import { useTranslation } from 'react-i18next' +import type { LivePreviewConfig } from '../../../../exports/config' import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from '../../../../exports/types' import type { EditViewProps } from '../types' @@ -11,6 +12,8 @@ import RenderFields from '../../forms/RenderFields' import { filterFields } from '../../forms/RenderFields/filterFields' import { fieldTypes } from '../../forms/field-types' import { LeaveWithoutSaving } from '../../modals/LeaveWithoutSaving' +import { useDocumentInfo } from '../../utilities/DocumentInfo' +import { useLocale } from '../../utilities/Locale' import Meta from '../../utilities/Meta' import { SetStepNav } from '../collections/Edit/SetStepNav' import { LivePreview } from './Preview' @@ -21,17 +24,28 @@ const baseClass = 'live-preview' export const LivePreviewView: React.FC = (props) => { const { i18n, t } = useTranslation('general') + const documentInfo = useDocumentInfo() + const locale = useLocale() - let url + let urlFromConfig: LivePreviewConfig['url'] if ('collection' in props) { - url = props?.collection.admin.livePreview.url + urlFromConfig = props?.collection.admin.livePreview.url } if ('global' in props) { - url = props?.global.admin.livePreview.url + urlFromConfig = props?.global.admin.livePreview.url } + const url = + typeof urlFromConfig === 'function' + ? urlFromConfig({ + data: props?.data, + documentInfo, + locale, + }) + : urlFromConfig + const popupState = usePopupWindow({ eventType: 'livePreview', href: url, diff --git a/packages/payload/src/collections/config/schema.ts b/packages/payload/src/collections/config/schema.ts index 5e15622785..884eaef710 100644 --- a/packages/payload/src/collections/config/schema.ts +++ b/packages/payload/src/collections/config/schema.ts @@ -68,7 +68,7 @@ const collectionSchema = joi.object().keys({ width: joi.alternatives().try(joi.number(), joi.string()), }), ), - url: joi.string(), + url: joi.alternatives().try(joi.string(), joi.func()), }), pagination: joi.object({ defaultLimit: joi.number(), diff --git a/packages/payload/src/collections/config/types.ts b/packages/payload/src/collections/config/types.ts index 0b3e4fb60a..7dad160537 100644 --- a/packages/payload/src/collections/config/types.ts +++ b/packages/payload/src/collections/config/types.ts @@ -19,7 +19,7 @@ import type { Endpoint, EntityDescription, GeneratePreviewURL, - LivePreview, + LivePreviewConfig, } from '../../config/types' import type { PayloadRequest, RequestContext } from '../../express/types' import type { Field } from '../../fields/config/types' @@ -273,7 +273,7 @@ export type CollectionAdminOptions = { /** * Live preview options */ - livePreview?: LivePreview + livePreview?: LivePreviewConfig pagination?: { defaultLimit?: number limits?: number[] diff --git a/packages/payload/src/config/types.ts b/packages/payload/src/config/types.ts index 37d6f6f52c..4acbaee994 100644 --- a/packages/payload/src/config/types.ts +++ b/packages/payload/src/config/types.ts @@ -13,6 +13,7 @@ import type { Configuration } from 'webpack' import type { DocumentTab } from '../admin/components/elements/DocumentHeader/Tabs/types' import type { RichTextAdapter } from '../admin/components/forms/field-types/RichText/types' +import type { ContextType } from '../admin/components/utilities/DocumentInfo/types' import type { CollectionEditViewProps, GlobalEditViewProps } from '../admin/components/views/types' import type { User } from '../auth/types' import type { PayloadBundler } from '../bundlers/types' @@ -40,7 +41,7 @@ type Email = { // eslint-disable-next-line no-use-before-define export type Plugin = (config: Config) => Config | Promise -export type LivePreview = { +export type LivePreviewConfig = { /** Device breakpoints to use for the `iframe` of the Live Preview window. Options are displayed in the Live Preview toolbar. @@ -58,7 +59,9 @@ export type LivePreview = { The frontend application is responsible for receiving the message and updating the UI accordingly. Use the `useLivePreview` hook to get started in React applications. */ - url?: string + url?: + | ((args: { data: Record; documentInfo: ContextType; locale: Locale }) => string) + | string } type GeneratePreviewURLOptions = { diff --git a/packages/payload/src/globals/config/schema.ts b/packages/payload/src/globals/config/schema.ts index 98b4ea2eee..83fb2f6f86 100644 --- a/packages/payload/src/globals/config/schema.ts +++ b/packages/payload/src/globals/config/schema.ts @@ -50,7 +50,7 @@ const globalSchema = joi width: joi.alternatives().try(joi.number(), joi.string()), }), ), - url: joi.string(), + url: joi.alternatives().try(joi.string(), joi.func()), }), preview: joi.func(), }), diff --git a/packages/payload/src/globals/config/types.ts b/packages/payload/src/globals/config/types.ts index 3110be871f..d84c1c1f02 100644 --- a/packages/payload/src/globals/config/types.ts +++ b/packages/payload/src/globals/config/types.ts @@ -15,12 +15,11 @@ import type { Endpoint, EntityDescription, GeneratePreviewURL, - LivePreview, + LivePreviewConfig, } from '../../config/types' import type { PayloadRequest } from '../../express/types' import type { Field } from '../../fields/config/types' import type { Where } from '../../types' - import type { IncomingGlobalVersions, SanitizedGlobalVersions } from '../../versions/types' export type TypeWithID = { @@ -80,6 +79,7 @@ export type GlobalAdminOptions = { */ Edit?: | { + [name: string]: EditView /** * Replace or modify individual nested routes, or add new ones: * + `Default` - `/admin/globals/:slug` @@ -93,7 +93,6 @@ export type GlobalAdminOptions = { */ Default?: EditView Versions?: EditView - [name: string]: EditView // TODO: uncomment these as they are built // API?: EditView // LivePreview?: EditView @@ -123,7 +122,7 @@ export type GlobalAdminOptions = { /** * Live preview options */ - livePreview?: LivePreview + livePreview?: LivePreviewConfig /** * Function to generate custom preview URL */ diff --git a/test/live-preview/collections/Pages.ts b/test/live-preview/collections/Pages.ts index 4b21de512c..8640a0f191 100644 --- a/test/live-preview/collections/Pages.ts +++ b/test/live-preview/collections/Pages.ts @@ -18,7 +18,7 @@ export const Pages: CollectionConfig = { }, admin: { livePreview: { - url: 'http://localhost:3001', + url: ({ data }) => `http://localhost:3001/${data?.slug}`, breakpoints: [ { label: 'Mobile', diff --git a/test/live-preview/collections/Posts.ts b/test/live-preview/collections/Posts.ts index f0bdc0011d..03dc64795f 100644 --- a/test/live-preview/collections/Posts.ts +++ b/test/live-preview/collections/Posts.ts @@ -18,7 +18,7 @@ export const Posts: CollectionConfig = { }, admin: { livePreview: { - url: 'http://localhost:3001', + url: ({ data, documentInfo }) => `http://localhost:3001/${documentInfo.slug}/${data?.slug}`, breakpoints: [ { label: 'Mobile', @@ -26,12 +26,6 @@ export const Posts: CollectionConfig = { width: 375, height: 667, }, - // { - // label: 'Desktop', - // name: 'desktop', - // width: 1440, - // height: 900, - // }, ], }, useAsTitle: 'title',