diff --git a/packages/live-preview/src/mergeData.ts b/packages/live-preview/src/mergeData.ts index 5fcfd9476..6c7a367a0 100644 --- a/packages/live-preview/src/mergeData.ts +++ b/packages/live-preview/src/mergeData.ts @@ -48,6 +48,8 @@ export const mergeData = async >(args: { data: { data: incomingData, depth, + // The incoming data already has had its locales flattened + flattenLocales: false, locale, }, endpoint: encodeURI( diff --git a/packages/payload/src/collections/endpoints/findByID.ts b/packages/payload/src/collections/endpoints/findByID.ts index 2b3119b93..be8e8a909 100644 --- a/packages/payload/src/collections/endpoints/findByID.ts +++ b/packages/payload/src/collections/endpoints/findByID.ts @@ -15,6 +15,12 @@ export const findByIDHandler: PayloadHandler = async (req) => { const { id, collection } = getRequestCollectionWithID(req) const depth = data ? data.depth : searchParams.get('depth') const trash = data ? data.trash : searchParams.get('trash') === 'true' + const flattenLocales = data + ? data.flattenLocales + : searchParams.has('flattenLocales') + ? searchParams.get('flattenLocales') === 'true' + : // flattenLocales should be undefined if not provided, so that the default (true) is applied in the operation + undefined const result = await findByIDOperation({ id, @@ -26,6 +32,7 @@ export const findByIDHandler: PayloadHandler = async (req) => { : undefined, depth: isNumber(depth) ? Number(depth) : undefined, draft: data ? data.draft : searchParams.get('draft') === 'true', + flattenLocales, joins: sanitizeJoinParams(req.query.joins as JoinParams), populate: sanitizePopulateParam(req.query.populate), req, diff --git a/packages/payload/src/collections/operations/findByID.ts b/packages/payload/src/collections/operations/findByID.ts index 518a41988..8912fe56e 100644 --- a/packages/payload/src/collections/operations/findByID.ts +++ b/packages/payload/src/collections/operations/findByID.ts @@ -20,7 +20,7 @@ import { combineQueries } from '../../database/combineQueries.js' import { sanitizeJoinQuery } from '../../database/sanitizeJoinQuery.js' import { sanitizeWhereQuery } from '../../database/sanitizeWhereQuery.js' import { NotFound } from '../../errors/index.js' -import { afterRead } from '../../fields/hooks/afterRead/index.js' +import { afterRead, type AfterReadArgs } from '../../fields/hooks/afterRead/index.js' import { validateQueryPaths } from '../../index.js' import { lockedDocumentsCollectionSlug } from '../../locked-documents/config.js' import { appendNonTrashedFilter } from '../../utilities/appendNonTrashedFilter.js' @@ -29,7 +29,7 @@ import { sanitizeSelect } from '../../utilities/sanitizeSelect.js' import { replaceWithDraftIfAvailable } from '../../versions/drafts/replaceWithDraftIfAvailable.js' import { buildAfterOperation } from './utils.js' -export type Arguments = { +export type FindByIDArgs = { collection: Collection currentDepth?: number /** @@ -49,14 +49,14 @@ export type Arguments = { select?: SelectType showHiddenFields?: boolean trash?: boolean -} +} & Pick, 'flattenLocales'> export const findByIDOperation = async < TSlug extends CollectionSlug, TDisableErrors extends boolean, TSelect extends SelectFromCollectionSlug, >( - incomingArgs: Arguments, + incomingArgs: FindByIDArgs, ): Promise, TDisableErrors>> => { let args = incomingArgs @@ -85,6 +85,7 @@ export const findByIDOperation = async < depth, disableErrors, draft: draftEnabled = false, + flattenLocales, includeLockStatus, joins, overrideAccess = false, @@ -279,6 +280,7 @@ export const findByIDOperation = async < doc: result, draft: draftEnabled, fallbackLocale: fallbackLocale!, + flattenLocales, global: null, locale: locale!, overrideAccess, diff --git a/packages/payload/src/collections/operations/local/findByID.ts b/packages/payload/src/collections/operations/local/findByID.ts index 0df8cc5a5..a576f1fbd 100644 --- a/packages/payload/src/collections/operations/local/findByID.ts +++ b/packages/payload/src/collections/operations/local/findByID.ts @@ -18,7 +18,7 @@ import type { SelectFromCollectionSlug } from '../../config/types.js' import { APIError } from '../../../errors/index.js' import { createLocalReq } from '../../../utilities/createLocalReq.js' -import { findByIDOperation } from '../findByID.js' +import { type FindByIDArgs, findByIDOperation } from '../findByID.js' export type Options< TSlug extends CollectionSlug, @@ -117,7 +117,7 @@ export type Options< * If you set `overrideAccess` to `false`, you can pass a user to use against the access control checks. */ user?: Document -} +} & Pick export async function findByIDLocal< TSlug extends CollectionSlug, @@ -135,6 +135,7 @@ export async function findByIDLocal< depth, disableErrors = false, draft = false, + flattenLocales, includeLockStatus, joins, overrideAccess = true, @@ -160,6 +161,7 @@ export async function findByIDLocal< depth, disableErrors, draft, + flattenLocales, includeLockStatus, joins, overrideAccess, diff --git a/packages/payload/src/fields/hooks/afterRead/index.ts b/packages/payload/src/fields/hooks/afterRead/index.ts index 9b5e6506c..9f5d8db92 100644 --- a/packages/payload/src/fields/hooks/afterRead/index.ts +++ b/packages/payload/src/fields/hooks/afterRead/index.ts @@ -6,7 +6,7 @@ import type { JsonObject, PayloadRequest, PopulateType, SelectType } from '../.. import { getSelectMode } from '../../../utilities/getSelectMode.js' import { traverseFields } from './traverseFields.js' -type Args = { +export type AfterReadArgs = { collection: null | SanitizedCollectionConfig context: RequestContext currentDepth?: number @@ -15,6 +15,12 @@ type Args = { draft: boolean fallbackLocale: null | string findMany?: boolean + /** + * Controls whether locales should be flattened into the requested locale. + * E.g.: { [locale]: fields } -> fields + * + * @default true + */ flattenLocales?: boolean global: null | SanitizedGlobalConfig locale: string @@ -35,7 +41,7 @@ type Args = { * - Populate relationships */ -export async function afterRead(args: Args): Promise { +export async function afterRead(args: AfterReadArgs): Promise { const { collection, context, diff --git a/packages/payload/src/fields/hooks/afterRead/promise.ts b/packages/payload/src/fields/hooks/afterRead/promise.ts index 04957f233..02b3b32e4 100644 --- a/packages/payload/src/fields/hooks/afterRead/promise.ts +++ b/packages/payload/src/fields/hooks/afterRead/promise.ts @@ -9,6 +9,7 @@ import type { SelectType, } from '../../../types/index.js' import type { Block, Field, TabAsField } from '../../config/types.js' +import type { AfterReadArgs } from './index.js' import { MissingEditorProp } from '../../../errors/index.js' import { type RequestContext } from '../../../index.js' @@ -40,7 +41,6 @@ type Args = { */ fieldPromises: Promise[] findMany: boolean - flattenLocales: boolean global: null | SanitizedGlobalConfig locale: null | string overrideAccess: boolean @@ -61,7 +61,7 @@ type Args = { siblingFields?: (Field | TabAsField)[] triggerAccessControl?: boolean triggerHooks?: boolean -} +} & Required, 'flattenLocales'>> // This function is responsible for the following actions, in order: // - Remove hidden fields from response @@ -236,15 +236,23 @@ export const promise = async ({ } } + // If locale is `all`, siblingDoc[field.name] will be an object mapping locales to values - locales won't be flattened. + // In this case, run the hook for each locale and value pair + const shouldRunHookOnAllLocales = + locale === 'all' && + 'name' in field && + typeof field.name === 'string' && + // If localized values were hoisted, siblingDoc[field.name] will not be an object mapping locales to values + // => Object.entries(siblingDoc[field.name]) will be the value of a single locale, not all locales + // => do not run the hook for each locale + !shouldHoistLocalizedValue && + fieldShouldBeLocalized({ field, parentIsLocalized: parentIsLocalized! }) && + typeof siblingDoc[field.name] === 'object' + if (fieldAffectsDataResult) { // Execute hooks if (triggerHooks && 'hooks' in field && field.hooks?.afterRead) { for (const hook of field.hooks.afterRead) { - const shouldRunHookOnAllLocales = - fieldShouldBeLocalized({ field, parentIsLocalized: parentIsLocalized! }) && - (locale === 'all' || !flattenLocales) && - typeof siblingDoc[field.name] === 'object' - if (shouldRunHookOnAllLocales) { const localesAndValues = Object.entries(siblingDoc[field.name]) await Promise.all( @@ -711,11 +719,6 @@ export const promise = async ({ if (editor?.hooks?.afterRead?.length) { for (const hook of editor.hooks.afterRead) { - const shouldRunHookOnAllLocales = - fieldShouldBeLocalized({ field, parentIsLocalized: parentIsLocalized! }) && - (locale === 'all' || !flattenLocales) && - typeof siblingDoc[field.name] === 'object' - if (shouldRunHookOnAllLocales) { const localesAndValues = Object.entries(siblingDoc[field.name]) diff --git a/packages/payload/src/globals/endpoints/findOne.ts b/packages/payload/src/globals/endpoints/findOne.ts index 0b051e7fe..eb0645559 100644 --- a/packages/payload/src/globals/endpoints/findOne.ts +++ b/packages/payload/src/globals/endpoints/findOne.ts @@ -13,6 +13,12 @@ export const findOneHandler: PayloadHandler = async (req) => { const globalConfig = getRequestGlobal(req) const { data, searchParams } = req const depth = data ? data.depth : searchParams.get('depth') + const flattenLocales = data + ? data.flattenLocales + : searchParams.has('flattenLocales') + ? searchParams.get('flattenLocales') === 'true' + : // flattenLocales should be undfined if not provided, so that the default (true) is applied in the operation + undefined const result = await findOneOperation({ slug: globalConfig.slug, @@ -23,6 +29,7 @@ export const findOneHandler: PayloadHandler = async (req) => { : undefined, depth: isNumber(depth) ? Number(depth) : undefined, draft: data ? data.draft : searchParams.get('draft') === 'true', + flattenLocales, globalConfig, populate: sanitizePopulateParam(req.query.populate), req, diff --git a/packages/payload/src/globals/operations/findOne.ts b/packages/payload/src/globals/operations/findOne.ts index 5e4536c74..fc817bbd1 100644 --- a/packages/payload/src/globals/operations/findOne.ts +++ b/packages/payload/src/globals/operations/findOne.ts @@ -9,14 +9,14 @@ import type { import type { SanitizedGlobalConfig } from '../config/types.js' import { executeAccess } from '../../auth/executeAccess.js' -import { afterRead } from '../../fields/hooks/afterRead/index.js' +import { afterRead, type AfterReadArgs } from '../../fields/hooks/afterRead/index.js' import { lockedDocumentsCollectionSlug } from '../../locked-documents/config.js' import { getSelectMode } from '../../utilities/getSelectMode.js' import { killTransaction } from '../../utilities/killTransaction.js' import { sanitizeSelect } from '../../utilities/sanitizeSelect.js' import { replaceWithDraftIfAvailable } from '../../versions/drafts/replaceWithDraftIfAvailable.js' -type Args = { +export type GlobalFindOneArgs = { /** * You may pass the document data directly which will skip the `db.findOne` database query. * This is useful if you want to use this endpoint solely for running hooks and populating data. @@ -32,15 +32,16 @@ type Args = { select?: SelectType showHiddenFields?: boolean slug: string -} +} & Pick, 'flattenLocales'> export const findOneOperation = async >( - args: Args, + args: GlobalFindOneArgs, ): Promise => { const { slug, depth, draft: draftEnabled = false, + flattenLocales, globalConfig, includeLockStatus, overrideAccess = false, @@ -206,6 +207,7 @@ export const findOneOperation = async >( doc, draft: draftEnabled, fallbackLocale: fallbackLocale!, + flattenLocales, global: globalConfig, locale: locale!, overrideAccess, diff --git a/packages/payload/src/globals/operations/local/findOne.ts b/packages/payload/src/globals/operations/local/findOne.ts index bae582cdf..f82b55466 100644 --- a/packages/payload/src/globals/operations/local/findOne.ts +++ b/packages/payload/src/globals/operations/local/findOne.ts @@ -11,7 +11,7 @@ import type { SelectFromGlobalSlug } from '../../config/types.js' import { APIError } from '../../../errors/index.js' import { createLocalReq } from '../../../utilities/createLocalReq.js' -import { findOneOperation } from '../findOne.js' +import { findOneOperation, type GlobalFindOneArgs } from '../findOne.js' export type Options = { /** @@ -78,7 +78,7 @@ export type Options = { * If you set `overrideAccess` to `false`, you can pass a user to use against the access control checks. */ user?: Document -} +} & Pick export async function findOneGlobalLocal< TSlug extends GlobalSlug, @@ -92,6 +92,7 @@ export async function findOneGlobalLocal< data, depth, draft = false, + flattenLocales, includeLockStatus, overrideAccess = true, populate, @@ -110,6 +111,7 @@ export async function findOneGlobalLocal< data, depth, draft, + flattenLocales, globalConfig, includeLockStatus, overrideAccess, diff --git a/test/live-preview/app/live-preview/_blocks/Relationships/index.tsx b/test/live-preview/app/live-preview/_blocks/Relationships/index.tsx index 20a53d10e..9926578d0 100644 --- a/test/live-preview/app/live-preview/_blocks/Relationships/index.tsx +++ b/test/live-preview/app/live-preview/_blocks/Relationships/index.tsx @@ -31,6 +31,12 @@ export const RelationshipsBlock: React.FC = (props) => {data?.richTextLexical && ( )} +

+ Rich Text — Lexical — Localized: +

+ {data?.richTextLexicalLocalized && ( + + )}

Upload:

diff --git a/test/live-preview/collections/Pages.ts b/test/live-preview/collections/Pages.ts index dd24a56fa..6d73c8dce 100644 --- a/test/live-preview/collections/Pages.ts +++ b/test/live-preview/collections/Pages.ts @@ -92,6 +92,18 @@ export const Pages: CollectionConfig = { ], }), }, + { + label: 'Rich Text — Lexical — Localized', + type: 'richText', + name: 'richTextLexicalLocalized', + localized: true, + editor: lexicalEditor({ + features: ({ defaultFeatures }) => [ + ...defaultFeatures, + BlocksFeature({ blocks: ['mediaBlock'] }), + ], + }), + }, { name: 'relationshipAsUpload', type: 'upload', diff --git a/test/live-preview/int.spec.ts b/test/live-preview/int.spec.ts index af338f8e8..72c818530 100644 --- a/test/live-preview/int.spec.ts +++ b/test/live-preview/int.spec.ts @@ -129,7 +129,13 @@ describe('Collections - Live Preview', () => { } as MessageEvent as LivePreviewMessageEvent, initialData: { title: 'Test Page', - id: 1, + id: 1 as number | string, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + hero: { + type: 'highImpact', + }, + slug: 'testPage', } as Page, }) @@ -177,6 +183,24 @@ describe('Collections - Live Preview', () => { expect(mergedData.title).toEqual('Test Page (Changed)') }) + it('— strings - merges localized data', async () => { + const initialData = await createPageWithInitialData({ + title: 'Test Page', + }) + + const mergedData = await mergeData({ + depth: 1, + incomingData: { + ...initialData, + localizedTitle: 'Test Page (Changed)', + }, + initialData, + collectionSlug: pagesSlug, + }) + + expect(mergedData.localizedTitle).toEqual('Test Page (Changed)') + }) + it('— arrays - can clear all rows', async () => { const initialData = await createPageWithInitialData({ title: 'Test Page', @@ -751,7 +775,7 @@ describe('Collections - Live Preview', () => { expect(merge2.richTextSlate[3].value).toMatchObject(media) }) - it('— relationships - populates within Lexical rich text editor', async () => { + async function lexicalTest(fieldName: string) { const initialData = await createPageWithInitialData({ title: 'Test Page', }) @@ -762,7 +786,7 @@ describe('Collections - Live Preview', () => { collectionSlug: pagesSlug, incomingData: { ...initialData, - richTextLexical: { + [fieldName]: { root: { type: 'root', format: '', @@ -804,12 +828,12 @@ describe('Collections - Live Preview', () => { initialData, }) - expect(merge1.richTextLexical.root.children).toHaveLength(3) - expect(merge1.richTextLexical.root.children[0].type).toEqual('relationship') - expect(merge1.richTextLexical.root.children[0].value).toMatchObject(testPost) - expect(merge1.richTextLexical.root.children[1].type).toEqual('paragraph') - expect(merge1.richTextLexical.root.children[2].type).toEqual('upload') - expect(merge1.richTextLexical.root.children[2].value).toMatchObject(media) + expect(merge1[fieldName].root.children).toHaveLength(3) + expect(merge1[fieldName].root.children[0].type).toEqual('relationship') + expect(merge1[fieldName].root.children[0].value).toMatchObject(testPost) + expect(merge1[fieldName].root.children[1].type).toEqual('paragraph') + expect(merge1[fieldName].root.children[2].type).toEqual('upload') + expect(merge1[fieldName].root.children[2].value).toMatchObject(media) // Add a node before the populated one const merge2 = await mergeData({ @@ -817,7 +841,7 @@ describe('Collections - Live Preview', () => { collectionSlug: pagesSlug, incomingData: { ...merge1, - richTextLexical: { + [fieldName]: { root: { type: 'root', format: '', @@ -867,13 +891,23 @@ describe('Collections - Live Preview', () => { initialData: merge1, }) - expect(merge2.richTextLexical.root.children).toHaveLength(4) - expect(merge2.richTextLexical.root.children[0].type).toEqual('relationship') - expect(merge2.richTextLexical.root.children[0].value).toMatchObject(testPost) - expect(merge2.richTextLexical.root.children[1].type).toEqual('paragraph') - expect(merge2.richTextLexical.root.children[2].type).toEqual('paragraph') - expect(merge2.richTextLexical.root.children[3].type).toEqual('upload') - expect(merge2.richTextLexical.root.children[3].value).toMatchObject(media) + expect(merge2[fieldName].root.children).toHaveLength(4) + expect(merge2[fieldName].root.children[0].type).toEqual('relationship') + expect(merge2[fieldName].root.children[0].value).toMatchObject(testPost) + expect(merge2[fieldName].root.children[1].type).toEqual('paragraph') + expect(merge2[fieldName].root.children[2].type).toEqual('paragraph') + expect(merge2[fieldName].root.children[3].type).toEqual('upload') + expect(merge2[fieldName].root.children[3].value).toMatchObject(media) + } + + // eslint-disable-next-line jest/expect-expect + it('— relationships - populates within Lexical rich text editor', async () => { + await lexicalTest('richTextLexical') + }) + + // eslint-disable-next-line jest/expect-expect + it('— relationships - populates within Localized Lexical rich text editor', async () => { + await lexicalTest('richTextLexicalLocalized') }) it('— relationships - re-populates externally updated relationships', async () => { diff --git a/test/live-preview/payload-types.ts b/test/live-preview/payload-types.ts index 0959a2c3e..a09cca017 100644 --- a/test/live-preview/payload-types.ts +++ b/test/live-preview/payload-types.ts @@ -98,7 +98,7 @@ export interface Config { 'payload-migrations': PayloadMigrationsSelect | PayloadMigrationsSelect; }; db: { - defaultIDType: number; + defaultIDType: string; }; globals: { header: Header; @@ -142,7 +142,7 @@ export interface UserAuthOperations { export interface MediaBlock { invertBackground?: boolean | null; position?: ('default' | 'fullscreen') | null; - media: number | Media; + media: string | Media; id?: string | null; blockName?: string | null; blockType: 'mediaBlock'; @@ -152,7 +152,7 @@ export interface MediaBlock { * via the `definition` "media". */ export interface Media { - id: number; + id: string; alt: string; updatedAt: string; createdAt: string; @@ -171,7 +171,7 @@ export interface Media { * via the `definition` "users". */ export interface User { - id: number; + id: string; updatedAt: string; createdAt: string; email: string; @@ -195,9 +195,9 @@ export interface User { * via the `definition` "pages". */ export interface Page { - id: number; + id: string; slug: string; - tenant?: (number | null) | Tenant; + tenant?: (string | null) | Tenant; title: string; hero: { type: 'none' | 'highImpact' | 'lowImpact'; @@ -206,7 +206,7 @@ export interface Page { [k: string]: unknown; }[] | null; - media?: (number | null) | Media; + media?: (string | null) | Media; }; layout?: | ( @@ -225,11 +225,11 @@ export interface Page { reference?: | ({ relationTo: 'posts'; - value: number | Post; + value: string | Post; } | null) | ({ relationTo: 'pages'; - value: number | Page; + value: string | Page; } | null); url?: string | null; label: string; @@ -262,11 +262,11 @@ export interface Page { reference?: | ({ relationTo: 'posts'; - value: number | Post; + value: string | Post; } | null) | ({ relationTo: 'pages'; - value: number | Page; + value: string | Page; } | null); url?: string | null; label: string; @@ -285,7 +285,7 @@ export interface Page { | { invertBackground?: boolean | null; position?: ('default' | 'fullscreen') | null; - media: number | Media; + media: string | Media; id?: string | null; blockName?: string | null; blockType: 'mediaBlock'; @@ -298,12 +298,12 @@ export interface Page { | null; populateBy?: ('collection' | 'selection') | null; relationTo?: 'posts' | null; - categories?: (number | Category)[] | null; + categories?: (string | Category)[] | null; limit?: number | null; selectedDocs?: | { relationTo: 'posts'; - value: number | Post; + value: string | Post; }[] | null; /** @@ -312,7 +312,7 @@ export interface Page { populatedDocs?: | { relationTo: 'posts'; - value: number | Post; + value: string | Post; }[] | null; /** @@ -326,7 +326,7 @@ export interface Page { )[] | null; localizedTitle?: string | null; - relationToLocalized?: (number | null) | Post; + relationToLocalized?: (string | null) | Post; richTextSlate?: | { [k: string]: unknown; @@ -347,22 +347,37 @@ export interface Page { }; [k: string]: unknown; } | null; - relationshipAsUpload?: (number | null) | Media; - relationshipMonoHasOne?: (number | null) | Post; - relationshipMonoHasMany?: (number | Post)[] | null; + richTextLexicalLocalized?: { + root: { + type: string; + children: { + type: string; + version: number; + [k: string]: unknown; + }[]; + direction: ('ltr' | 'rtl') | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + [k: string]: unknown; + } | null; + relationshipAsUpload?: (string | null) | Media; + relationshipMonoHasOne?: (string | null) | Post; + relationshipMonoHasMany?: (string | Post)[] | null; relationshipPolyHasOne?: { relationTo: 'posts'; - value: number | Post; + value: string | Post; } | null; relationshipPolyHasMany?: | { relationTo: 'posts'; - value: number | Post; + value: string | Post; }[] | null; arrayOfRelationships?: | { - uploadInArray?: (number | null) | Media; + uploadInArray?: (string | null) | Media; richTextInArray?: { root: { type: string; @@ -378,28 +393,28 @@ export interface Page { }; [k: string]: unknown; } | null; - relationshipInArrayMonoHasOne?: (number | null) | Post; - relationshipInArrayMonoHasMany?: (number | Post)[] | null; + relationshipInArrayMonoHasOne?: (string | null) | Post; + relationshipInArrayMonoHasMany?: (string | Post)[] | null; relationshipInArrayPolyHasOne?: { relationTo: 'posts'; - value: number | Post; + value: string | Post; } | null; relationshipInArrayPolyHasMany?: | { relationTo: 'posts'; - value: number | Post; + value: string | Post; }[] | null; id?: string | null; }[] | null; tab?: { - relationshipInTab?: (number | null) | Post; + relationshipInTab?: (string | null) | Post; }; meta?: { title?: string | null; description?: string | null; - image?: (number | null) | Media; + image?: (string | null) | Media; }; updatedAt: string; createdAt: string; @@ -409,7 +424,7 @@ export interface Page { * via the `definition` "tenants". */ export interface Tenant { - id: number; + id: string; title: string; clientURL: string; updatedAt: string; @@ -420,9 +435,9 @@ export interface Tenant { * via the `definition` "posts". */ export interface Post { - id: number; + id: string; slug: string; - tenant?: (number | null) | Tenant; + tenant?: (string | null) | Tenant; title: string; hero: { type: 'none' | 'highImpact' | 'lowImpact'; @@ -431,7 +446,7 @@ export interface Post { [k: string]: unknown; }[] | null; - media?: (number | null) | Media; + media?: (string | null) | Media; }; layout?: | ( @@ -450,11 +465,11 @@ export interface Post { reference?: | ({ relationTo: 'posts'; - value: number | Post; + value: string | Post; } | null) | ({ relationTo: 'pages'; - value: number | Page; + value: string | Page; } | null); url?: string | null; label: string; @@ -487,11 +502,11 @@ export interface Post { reference?: | ({ relationTo: 'posts'; - value: number | Post; + value: string | Post; } | null) | ({ relationTo: 'pages'; - value: number | Page; + value: string | Page; } | null); url?: string | null; label: string; @@ -510,7 +525,7 @@ export interface Post { | { invertBackground?: boolean | null; position?: ('default' | 'fullscreen') | null; - media: number | Media; + media: string | Media; id?: string | null; blockName?: string | null; blockType: 'mediaBlock'; @@ -523,12 +538,12 @@ export interface Post { | null; populateBy?: ('collection' | 'selection') | null; relationTo?: 'posts' | null; - categories?: (number | Category)[] | null; + categories?: (string | Category)[] | null; limit?: number | null; selectedDocs?: | { relationTo: 'posts'; - value: number | Post; + value: string | Post; }[] | null; /** @@ -537,7 +552,7 @@ export interface Post { populatedDocs?: | { relationTo: 'posts'; - value: number | Post; + value: string | Post; }[] | null; /** @@ -550,12 +565,12 @@ export interface Post { } )[] | null; - relatedPosts?: (number | Post)[] | null; + relatedPosts?: (string | Post)[] | null; localizedTitle?: string | null; meta?: { title?: string | null; description?: string | null; - image?: (number | null) | Media; + image?: (string | null) | Media; }; updatedAt: string; createdAt: string; @@ -566,7 +581,7 @@ export interface Post { * via the `definition` "categories". */ export interface Category { - id: number; + id: string; title?: string | null; updatedAt: string; createdAt: string; @@ -576,9 +591,9 @@ export interface Category { * via the `definition` "ssr". */ export interface Ssr { - id: number; + id: string; slug: string; - tenant?: (number | null) | Tenant; + tenant?: (string | null) | Tenant; title: string; hero: { type: 'none' | 'highImpact' | 'lowImpact'; @@ -587,7 +602,7 @@ export interface Ssr { [k: string]: unknown; }[] | null; - media?: (number | null) | Media; + media?: (string | null) | Media; }; layout?: | ( @@ -606,11 +621,11 @@ export interface Ssr { reference?: | ({ relationTo: 'posts'; - value: number | Post; + value: string | Post; } | null) | ({ relationTo: 'pages'; - value: number | Page; + value: string | Page; } | null); url?: string | null; label: string; @@ -643,11 +658,11 @@ export interface Ssr { reference?: | ({ relationTo: 'posts'; - value: number | Post; + value: string | Post; } | null) | ({ relationTo: 'pages'; - value: number | Page; + value: string | Page; } | null); url?: string | null; label: string; @@ -666,7 +681,7 @@ export interface Ssr { | { invertBackground?: boolean | null; position?: ('default' | 'fullscreen') | null; - media: number | Media; + media: string | Media; id?: string | null; blockName?: string | null; blockType: 'mediaBlock'; @@ -679,12 +694,12 @@ export interface Ssr { | null; populateBy?: ('collection' | 'selection') | null; relationTo?: 'posts' | null; - categories?: (number | Category)[] | null; + categories?: (string | Category)[] | null; limit?: number | null; selectedDocs?: | { relationTo: 'posts'; - value: number | Post; + value: string | Post; }[] | null; /** @@ -693,7 +708,7 @@ export interface Ssr { populatedDocs?: | { relationTo: 'posts'; - value: number | Post; + value: string | Post; }[] | null; /** @@ -709,7 +724,7 @@ export interface Ssr { meta?: { title?: string | null; description?: string | null; - image?: (number | null) | Media; + image?: (string | null) | Media; }; updatedAt: string; createdAt: string; @@ -719,9 +734,9 @@ export interface Ssr { * via the `definition` "ssr-autosave". */ export interface SsrAutosave { - id: number; + id: string; slug: string; - tenant?: (number | null) | Tenant; + tenant?: (string | null) | Tenant; title: string; hero: { type: 'none' | 'highImpact' | 'lowImpact'; @@ -730,7 +745,7 @@ export interface SsrAutosave { [k: string]: unknown; }[] | null; - media?: (number | null) | Media; + media?: (string | null) | Media; }; layout?: | ( @@ -749,11 +764,11 @@ export interface SsrAutosave { reference?: | ({ relationTo: 'posts'; - value: number | Post; + value: string | Post; } | null) | ({ relationTo: 'pages'; - value: number | Page; + value: string | Page; } | null); url?: string | null; label: string; @@ -786,11 +801,11 @@ export interface SsrAutosave { reference?: | ({ relationTo: 'posts'; - value: number | Post; + value: string | Post; } | null) | ({ relationTo: 'pages'; - value: number | Page; + value: string | Page; } | null); url?: string | null; label: string; @@ -809,7 +824,7 @@ export interface SsrAutosave { | { invertBackground?: boolean | null; position?: ('default' | 'fullscreen') | null; - media: number | Media; + media: string | Media; id?: string | null; blockName?: string | null; blockType: 'mediaBlock'; @@ -822,12 +837,12 @@ export interface SsrAutosave { | null; populateBy?: ('collection' | 'selection') | null; relationTo?: 'posts' | null; - categories?: (number | Category)[] | null; + categories?: (string | Category)[] | null; limit?: number | null; selectedDocs?: | { relationTo: 'posts'; - value: number | Post; + value: string | Post; }[] | null; /** @@ -836,7 +851,7 @@ export interface SsrAutosave { populatedDocs?: | { relationTo: 'posts'; - value: number | Post; + value: string | Post; }[] | null; /** @@ -852,7 +867,7 @@ export interface SsrAutosave { meta?: { title?: string | null; description?: string | null; - image?: (number | null) | Media; + image?: (string | null) | Media; }; updatedAt: string; createdAt: string; @@ -865,7 +880,7 @@ export interface SsrAutosave { * via the `definition` "collection-level-config". */ export interface CollectionLevelConfig { - id: number; + id: string; title?: string | null; updatedAt: string; createdAt: string; @@ -875,48 +890,48 @@ export interface CollectionLevelConfig { * via the `definition` "payload-locked-documents". */ export interface PayloadLockedDocument { - id: number; + id: string; document?: | ({ relationTo: 'users'; - value: number | User; + value: string | User; } | null) | ({ relationTo: 'pages'; - value: number | Page; + value: string | Page; } | null) | ({ relationTo: 'posts'; - value: number | Post; + value: string | Post; } | null) | ({ relationTo: 'ssr'; - value: number | Ssr; + value: string | Ssr; } | null) | ({ relationTo: 'ssr-autosave'; - value: number | SsrAutosave; + value: string | SsrAutosave; } | null) | ({ relationTo: 'tenants'; - value: number | Tenant; + value: string | Tenant; } | null) | ({ relationTo: 'categories'; - value: number | Category; + value: string | Category; } | null) | ({ relationTo: 'media'; - value: number | Media; + value: string | Media; } | null) | ({ relationTo: 'collection-level-config'; - value: number | CollectionLevelConfig; + value: string | CollectionLevelConfig; } | null); globalSlug?: string | null; user: { relationTo: 'users'; - value: number | User; + value: string | User; }; updatedAt: string; createdAt: string; @@ -926,10 +941,10 @@ export interface PayloadLockedDocument { * via the `definition` "payload-preferences". */ export interface PayloadPreference { - id: number; + id: string; user: { relationTo: 'users'; - value: number | User; + value: string | User; }; key?: string | null; value?: @@ -949,7 +964,7 @@ export interface PayloadPreference { * via the `definition` "payload-migrations". */ export interface PayloadMigration { - id: number; + id: string; name?: string | null; batch?: number | null; updatedAt: string; @@ -1071,6 +1086,7 @@ export interface PagesSelect { relationToLocalized?: T; richTextSlate?: T; richTextLexical?: T; + richTextLexicalLocalized?: T; relationshipAsUpload?: T; relationshipMonoHasOne?: T; relationshipMonoHasMany?: T; @@ -1489,7 +1505,7 @@ export interface PayloadMigrationsSelect { * via the `definition` "header". */ export interface Header { - id: number; + id: string; navItems?: | { link: { @@ -1498,11 +1514,11 @@ export interface Header { reference?: | ({ relationTo: 'posts'; - value: number | Post; + value: string | Post; } | null) | ({ relationTo: 'pages'; - value: number | Page; + value: string | Page; } | null); url?: string | null; label: string; @@ -1522,7 +1538,7 @@ export interface Header { * via the `definition` "footer". */ export interface Footer { - id: number; + id: string; navItems?: | { link: { @@ -1531,11 +1547,11 @@ export interface Footer { reference?: | ({ relationTo: 'posts'; - value: number | Post; + value: string | Post; } | null) | ({ relationTo: 'pages'; - value: number | Page; + value: string | Page; } | null); url?: string | null; label: string; diff --git a/test/live-preview/seed/home.ts b/test/live-preview/seed/home.ts index 3814020b0..99d16e180 100644 --- a/test/live-preview/seed/home.ts +++ b/test/live-preview/seed/home.ts @@ -9,6 +9,7 @@ export const home: Omit = { description: 'This is an example of live preview on a page.', }, tenant: '{{TENANT_1_ID}}', + localizedTitle: 'Localized Title', hero: { type: 'highImpact', richText: [ @@ -169,6 +170,35 @@ export const home: Omit = { direction: null, }, }, + richTextLexicalLocalized: { + root: { + type: 'root', + format: '', + indent: 0, + version: 1, + children: [ + { + children: [], + direction: null, + format: '', + indent: 0, + type: 'paragraph', + version: 1, + }, + { + format: '', + type: 'upload', + version: 1, + fields: null, + relationTo: 'media', + value: { + id: '{{MEDIA_ID}}', + }, + }, + ], + direction: null, + }, + }, relationshipMonoHasMany: ['{{POST_1_ID}}'], relationshipMonoHasOne: '{{POST_1_ID}}', relationshipPolyHasMany: [{ relationTo: 'posts', value: '{{POST_1_ID}}' }],