diff --git a/packages/live-preview/src/traverseFields.ts b/packages/live-preview/src/traverseFields.ts index abee1f26a..af6b0753a 100644 --- a/packages/live-preview/src/traverseFields.ts +++ b/packages/live-preview/src/traverseFields.ts @@ -1,6 +1,7 @@ import type { fieldSchemaToJSON } from 'payload/utilities' import { promise } from './promise' +import { traverseRichText } from './traverseRichText' export const traverseFields = (args: { apiRoute?: string @@ -26,6 +27,18 @@ export const traverseFields = (args: { const fieldName = fieldSchema.name switch (fieldSchema.type) { + case 'richText': + result[fieldName] = traverseRichText({ + apiRoute, + depth, + incomingData: incomingData[fieldName], + populationPromises, + result: result[fieldName], + serverURL, + }) + + break + case 'array': if (Array.isArray(incomingData[fieldName])) { result[fieldName] = incomingData[fieldName].map((incomingRow, i) => { @@ -50,6 +63,7 @@ export const traverseFields = (args: { return result[fieldName][i] }) } + break case 'blocks': diff --git a/packages/live-preview/src/traverseRichText.ts b/packages/live-preview/src/traverseRichText.ts new file mode 100644 index 000000000..625bd1dbf --- /dev/null +++ b/packages/live-preview/src/traverseRichText.ts @@ -0,0 +1,63 @@ +import { promise } from './promise' + +export const traverseRichText = ({ + apiRoute, + depth, + incomingData, + populationPromises, + result, + serverURL, +}: { + apiRoute: string + depth: number + incomingData: any + populationPromises: Promise[] + result: any + serverURL: string +}): any => { + if (Array.isArray(incomingData)) { + result = incomingData.map((incomingRow) => + traverseRichText({ + apiRoute, + depth, + incomingData: incomingRow, + populationPromises, + result, + serverURL, + }), + ) + } else if (typeof incomingData === 'object' && incomingData !== null) { + result = incomingData + + if ('relationTo' in incomingData && 'value' in incomingData && incomingData.value) { + populationPromises.push( + promise({ + id: typeof incomingData.value === 'object' ? incomingData.value.id : incomingData.value, + accessor: 'value', + apiRoute, + collection: String(incomingData.relationTo), + depth, + ref: result, + serverURL, + }), + ) + } else { + result = {} + + Object.keys(incomingData).forEach((key) => { + result[key] = traverseRichText({ + apiRoute, + depth, + incomingData: incomingData[key], + populationPromises, + result, + serverURL, + }) + }) + } + } else { + result = incomingData + } + + return result +} diff --git a/test/live-preview/int.spec.ts b/test/live-preview/int.spec.ts index 6e24babfa..03f0458e2 100644 --- a/test/live-preview/int.spec.ts +++ b/test/live-preview/int.spec.ts @@ -384,6 +384,127 @@ describe('Collections - Live Preview', () => { ]) }) + it('— relationships - populates within rich text', async () => { + const initialData: Partial = { + title: 'Test Page', + } + + // Add a relationship + const merge1 = await mergeData({ + depth: 1, + fieldSchema: schemaJSON, + incomingData: { + ...initialData, + relationshipInRichText: [ + { + type: 'paragraph', + text: 'Paragraph 1', + }, + { + type: 'reference', + reference: { + relationTo: 'posts', + value: testPost.id, + }, + }, + ], + }, + initialData, + serverURL, + returnNumberOfRequests: true, + }) + + expect(merge1._numberOfRequests).toEqual(1) + expect(merge1.relationshipInRichText).toHaveLength(2) + expect(merge1.relationshipInRichText[1].reference.value).toMatchObject(testPost) + + // Remove the relationship + const merge2 = await mergeData({ + depth: 1, + fieldSchema: schemaJSON, + incomingData: { + ...merge1, + relationshipInRichText: [ + { + type: 'paragraph', + text: 'Paragraph 1', + }, + ], + }, + initialData, + serverURL, + returnNumberOfRequests: true, + }) + + expect(merge2._numberOfRequests).toEqual(0) + expect(merge2.relationshipInRichText).toHaveLength(1) + expect(merge2.relationshipInRichText[0].type).toEqual('paragraph') + }) + + it('— rich text - merges rich text', async () => { + const initialData: Partial = { + title: 'Test Page', + } + + // Add a relationship + const merge1 = await mergeData({ + depth: 1, + fieldSchema: schemaJSON, + incomingData: { + ...initialData, + hero: { + type: 'lowImpact', + richText: [ + { + type: 'paragraph', + children: [ + { + text: 'Paragraph 1', + }, + ], + }, + ], + }, + }, + initialData, + serverURL, + returnNumberOfRequests: true, + }) + + expect(merge1._numberOfRequests).toEqual(0) + expect(merge1.hero.richText).toHaveLength(1) + expect(merge1.hero.richText[0].children[0].text).toEqual('Paragraph 1') + + // Update the rich text + const merge2 = await mergeData({ + depth: 1, + fieldSchema: schemaJSON, + incomingData: { + ...merge1, + hero: { + type: 'lowImpact', + richText: [ + { + type: 'paragraph', + children: [ + { + text: 'Paragraph 1 (Updated)', + }, + ], + }, + ], + }, + }, + initialData, + serverURL, + returnNumberOfRequests: true, + }) + + expect(merge2._numberOfRequests).toEqual(0) + expect(merge2.hero.richText).toHaveLength(1) + expect(merge2.hero.richText[0].children[0].text).toEqual('Paragraph 1 (Updated)') + }) + it('— relationships - populates within blocks', async () => { const block1 = (shallow?: boolean): Extract => ({ blockType: 'cta', diff --git a/test/live-preview/next-app/app/_blocks/Relationships/index.tsx b/test/live-preview/next-app/app/_blocks/Relationships/index.tsx index 50659f01f..a68f3bbb1 100644 --- a/test/live-preview/next-app/app/_blocks/Relationships/index.tsx +++ b/test/live-preview/next-app/app/_blocks/Relationships/index.tsx @@ -21,88 +21,124 @@ export const RelationshipsBlock: React.FC = (props) =>

This block is for testing purposes only. It renders every possible type of relationship.

-

Upload:

- {data?.relationshipAsUpload && ( +

+ Rich Text: +

+ {data?.relationshipInRichText && } +

+ Upload: +

+ {data?.relationshipAsUpload ? (
{typeof data?.relationshipAsUpload === 'string' ? data?.relationshipAsUpload : data?.relationshipAsUpload.filename}
+ ) : ( +
None
)} -

Rich Text:

- {data?.relationshipInRichText && } -

Monomorphic Has One:

- {data?.relationshipMonoHasOne && ( +

+ Monomorphic Has One: +

+ {data?.relationshipMonoHasOne ? (
{typeof data?.relationshipMonoHasOne === 'string' ? data?.relationshipMonoHasOne : data?.relationshipMonoHasOne.title}
+ ) : ( +
None
)} -

Monomorphic Has Many:

- {data?.relationshipMonoHasMany?.map( - (item, index) => - item &&
{typeof item === 'string' ? item : item.title}
, +

+ Monomorphic Has Many: +

+ {data?.relationshipMonoHasMany?.map((item, index) => + item ?
{typeof item === 'string' ? item : item.title}
: 'null', )} -

Polymorphic Has One:

- {data?.relationshipPolyHasOne && ( +

+ Polymorphic Has One: +

+ {data?.relationshipPolyHasOne ? (
{typeof data?.relationshipPolyHasOne.value === 'string' ? data?.relationshipPolyHasOne.value : data?.relationshipPolyHasOne.value.title}
+ ) : ( +
None
)} -

Polymorphic Has Many:

- {data?.relationshipPolyHasMany?.map( - (item, index) => - item.value && ( -
- {typeof item.value === 'string' ? item.value : item.value.title} -
- ), +

+ Polymorphic Has Many: +

+ {data?.relationshipPolyHasMany?.map((item, index) => + item.value ? ( +
{typeof item.value === 'string' ? item.value : item.value.title}
+ ) : ( + 'null' + ), )} -

Array of Relationships:

+

+ Array of Relationships: +

{data?.arrayOfRelationships?.map((item, index) => (
-

Upload:

- {item?.uploadInArray && ( +

+ Rich Text: +

+ {item?.richTextInArray && } +

+ Upload: +

+ {item?.uploadInArray ? (
{typeof item?.uploadInArray === 'string' ? item?.uploadInArray : item?.uploadInArray.filename}
+ ) : ( +
None
)} -

Rich Text:

- {item?.richTextInArray && } -

Monomorphic Has One:

- {item?.relationshipInArrayMonoHasOne && ( +

+ Monomorphic Has One: +

+ {item?.relationshipInArrayMonoHasOne ? (
{typeof item?.relationshipInArrayMonoHasOne === 'string' ? item?.relationshipInArrayMonoHasOne : item?.relationshipInArrayMonoHasOne.title}
+ ) : ( +
None
)} -

Monomorphic Has Many:

- {item?.relationshipInArrayMonoHasMany?.map( - (rel, relIndex) => - rel &&
{typeof rel === 'string' ? rel : rel.title}
, +

+ Monomorphic Has Many: +

+ {item?.relationshipInArrayMonoHasMany?.map((rel, relIndex) => + rel ?
{typeof rel === 'string' ? rel : rel.title}
: 'null', )} -

Polymorphic Has One:

- {item?.relationshipInArrayPolyHasOne && ( +

+ Polymorphic Has One: +

+ {item?.relationshipInArrayPolyHasOne ? (
{typeof item?.relationshipInArrayPolyHasOne.value === 'string' ? item?.relationshipInArrayPolyHasOne.value : item?.relationshipInArrayPolyHasOne.value.title}
+ ) : ( +
None
)} -

Polymorphic Has Many:

- {item?.relationshipInArrayPolyHasMany?.map( - (rel, relIndex) => - rel.value && ( -
- {typeof rel.value === 'string' ? rel.value : rel.value.title} -
- ), +

+ Polymorphic Has Many: +

+ {item?.relationshipInArrayPolyHasMany?.map((rel, relIndex) => + rel.value ? ( +
+ {typeof rel.value === 'string' ? rel.value : rel.value.title} +
+ ) : ( + 'null' + ), )}
))} diff --git a/test/live-preview/next-app/app/_components/RichText/serialize.tsx b/test/live-preview/next-app/app/_components/RichText/serialize.tsx index c111a6d6a..3c8e30970 100644 --- a/test/live-preview/next-app/app/_components/RichText/serialize.tsx +++ b/test/live-preview/next-app/app/_components/RichText/serialize.tsx @@ -78,7 +78,9 @@ const serialize = (children?: Children): React.ReactNode[] => case 'relationship': return ( - {node.value && typeof node.value === 'object' ? node.value.title : node.value} + {node.value && typeof node.value === 'object' + ? node.value.title || node.value.id + : node.value} ) case 'link':