fix(live-preview): populates rich text relationships
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import type { fieldSchemaToJSON } from 'payload/utilities'
|
import type { fieldSchemaToJSON } from 'payload/utilities'
|
||||||
|
|
||||||
import { promise } from './promise'
|
import { promise } from './promise'
|
||||||
|
import { traverseRichText } from './traverseRichText'
|
||||||
|
|
||||||
export const traverseFields = <T>(args: {
|
export const traverseFields = <T>(args: {
|
||||||
apiRoute?: string
|
apiRoute?: string
|
||||||
@@ -26,6 +27,18 @@ export const traverseFields = <T>(args: {
|
|||||||
const fieldName = fieldSchema.name
|
const fieldName = fieldSchema.name
|
||||||
|
|
||||||
switch (fieldSchema.type) {
|
switch (fieldSchema.type) {
|
||||||
|
case 'richText':
|
||||||
|
result[fieldName] = traverseRichText({
|
||||||
|
apiRoute,
|
||||||
|
depth,
|
||||||
|
incomingData: incomingData[fieldName],
|
||||||
|
populationPromises,
|
||||||
|
result: result[fieldName],
|
||||||
|
serverURL,
|
||||||
|
})
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
case 'array':
|
case 'array':
|
||||||
if (Array.isArray(incomingData[fieldName])) {
|
if (Array.isArray(incomingData[fieldName])) {
|
||||||
result[fieldName] = incomingData[fieldName].map((incomingRow, i) => {
|
result[fieldName] = incomingData[fieldName].map((incomingRow, i) => {
|
||||||
@@ -50,6 +63,7 @@ export const traverseFields = <T>(args: {
|
|||||||
return result[fieldName][i]
|
return result[fieldName][i]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'blocks':
|
case 'blocks':
|
||||||
|
|||||||
63
packages/live-preview/src/traverseRichText.ts
Normal file
63
packages/live-preview/src/traverseRichText.ts
Normal file
@@ -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<void>[]
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -384,6 +384,127 @@ describe('Collections - Live Preview', () => {
|
|||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('— relationships - populates within rich text', async () => {
|
||||||
|
const initialData: Partial<Page> = {
|
||||||
|
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<Page> = {
|
||||||
|
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 () => {
|
it('— relationships - populates within blocks', async () => {
|
||||||
const block1 = (shallow?: boolean): Extract<Page['layout'][0], { blockType: 'cta' }> => ({
|
const block1 = (shallow?: boolean): Extract<Page['layout'][0], { blockType: 'cta' }> => ({
|
||||||
blockType: 'cta',
|
blockType: 'cta',
|
||||||
|
|||||||
@@ -21,88 +21,124 @@ export const RelationshipsBlock: React.FC<RelationshipsBlockProps> = (props) =>
|
|||||||
<p>
|
<p>
|
||||||
This block is for testing purposes only. It renders every possible type of relationship.
|
This block is for testing purposes only. It renders every possible type of relationship.
|
||||||
</p>
|
</p>
|
||||||
<p>Upload:</p>
|
<p>
|
||||||
{data?.relationshipAsUpload && (
|
<b>Rich Text:</b>
|
||||||
|
</p>
|
||||||
|
{data?.relationshipInRichText && <RichText content={data.relationshipInRichText} />}
|
||||||
|
<p>
|
||||||
|
<b>Upload:</b>
|
||||||
|
</p>
|
||||||
|
{data?.relationshipAsUpload ? (
|
||||||
<div>
|
<div>
|
||||||
{typeof data?.relationshipAsUpload === 'string'
|
{typeof data?.relationshipAsUpload === 'string'
|
||||||
? data?.relationshipAsUpload
|
? data?.relationshipAsUpload
|
||||||
: data?.relationshipAsUpload.filename}
|
: data?.relationshipAsUpload.filename}
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>None</div>
|
||||||
)}
|
)}
|
||||||
<p>Rich Text:</p>
|
<p>
|
||||||
{data?.relationshipInRichText && <RichText content={data.relationshipInRichText} />}
|
<b>Monomorphic Has One:</b>
|
||||||
<p>Monomorphic Has One:</p>
|
</p>
|
||||||
{data?.relationshipMonoHasOne && (
|
{data?.relationshipMonoHasOne ? (
|
||||||
<div>
|
<div>
|
||||||
{typeof data?.relationshipMonoHasOne === 'string'
|
{typeof data?.relationshipMonoHasOne === 'string'
|
||||||
? data?.relationshipMonoHasOne
|
? data?.relationshipMonoHasOne
|
||||||
: data?.relationshipMonoHasOne.title}
|
: data?.relationshipMonoHasOne.title}
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>None</div>
|
||||||
)}
|
)}
|
||||||
<p>Monomorphic Has Many:</p>
|
<p>
|
||||||
{data?.relationshipMonoHasMany?.map(
|
<b>Monomorphic Has Many:</b>
|
||||||
(item, index) =>
|
</p>
|
||||||
item && <div key={index}>{typeof item === 'string' ? item : item.title}</div>,
|
{data?.relationshipMonoHasMany?.map((item, index) =>
|
||||||
|
item ? <div key={index}>{typeof item === 'string' ? item : item.title}</div> : 'null',
|
||||||
)}
|
)}
|
||||||
<p>Polymorphic Has One:</p>
|
<p>
|
||||||
{data?.relationshipPolyHasOne && (
|
<b>Polymorphic Has One:</b>
|
||||||
|
</p>
|
||||||
|
{data?.relationshipPolyHasOne ? (
|
||||||
<div>
|
<div>
|
||||||
{typeof data?.relationshipPolyHasOne.value === 'string'
|
{typeof data?.relationshipPolyHasOne.value === 'string'
|
||||||
? data?.relationshipPolyHasOne.value
|
? data?.relationshipPolyHasOne.value
|
||||||
: data?.relationshipPolyHasOne.value.title}
|
: data?.relationshipPolyHasOne.value.title}
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>None</div>
|
||||||
)}
|
)}
|
||||||
<p>Polymorphic Has Many:</p>
|
<p>
|
||||||
{data?.relationshipPolyHasMany?.map(
|
<b>Polymorphic Has Many:</b>
|
||||||
(item, index) =>
|
</p>
|
||||||
item.value && (
|
{data?.relationshipPolyHasMany?.map((item, index) =>
|
||||||
<div key={index}>
|
item.value ? (
|
||||||
{typeof item.value === 'string' ? item.value : item.value.title}
|
<div key={index}>{typeof item.value === 'string' ? item.value : item.value.title}</div>
|
||||||
</div>
|
) : (
|
||||||
),
|
'null'
|
||||||
|
),
|
||||||
)}
|
)}
|
||||||
<p>Array of Relationships:</p>
|
<p>
|
||||||
|
<b>Array of Relationships:</b>
|
||||||
|
</p>
|
||||||
{data?.arrayOfRelationships?.map((item, index) => (
|
{data?.arrayOfRelationships?.map((item, index) => (
|
||||||
<div className={classes.array} key={index}>
|
<div className={classes.array} key={index}>
|
||||||
<p>Upload:</p>
|
<p>
|
||||||
{item?.uploadInArray && (
|
<b>Rich Text:</b>
|
||||||
|
</p>
|
||||||
|
{item?.richTextInArray && <RichText content={item.richTextInArray} />}
|
||||||
|
<p>
|
||||||
|
<b>Upload:</b>
|
||||||
|
</p>
|
||||||
|
{item?.uploadInArray ? (
|
||||||
<div>
|
<div>
|
||||||
{typeof item?.uploadInArray === 'string'
|
{typeof item?.uploadInArray === 'string'
|
||||||
? item?.uploadInArray
|
? item?.uploadInArray
|
||||||
: item?.uploadInArray.filename}
|
: item?.uploadInArray.filename}
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>None</div>
|
||||||
)}
|
)}
|
||||||
<p>Rich Text:</p>
|
<p>
|
||||||
{item?.richTextInArray && <RichText content={item.richTextInArray} />}
|
<b>Monomorphic Has One:</b>
|
||||||
<p>Monomorphic Has One:</p>
|
</p>
|
||||||
{item?.relationshipInArrayMonoHasOne && (
|
{item?.relationshipInArrayMonoHasOne ? (
|
||||||
<div>
|
<div>
|
||||||
{typeof item?.relationshipInArrayMonoHasOne === 'string'
|
{typeof item?.relationshipInArrayMonoHasOne === 'string'
|
||||||
? item?.relationshipInArrayMonoHasOne
|
? item?.relationshipInArrayMonoHasOne
|
||||||
: item?.relationshipInArrayMonoHasOne.title}
|
: item?.relationshipInArrayMonoHasOne.title}
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>None</div>
|
||||||
)}
|
)}
|
||||||
<p>Monomorphic Has Many:</p>
|
<p>
|
||||||
{item?.relationshipInArrayMonoHasMany?.map(
|
<b>Monomorphic Has Many:</b>
|
||||||
(rel, relIndex) =>
|
</p>
|
||||||
rel && <div key={relIndex}>{typeof rel === 'string' ? rel : rel.title}</div>,
|
{item?.relationshipInArrayMonoHasMany?.map((rel, relIndex) =>
|
||||||
|
rel ? <div key={relIndex}>{typeof rel === 'string' ? rel : rel.title}</div> : 'null',
|
||||||
)}
|
)}
|
||||||
<p>Polymorphic Has One:</p>
|
<p>
|
||||||
{item?.relationshipInArrayPolyHasOne && (
|
<b>Polymorphic Has One:</b>
|
||||||
|
</p>
|
||||||
|
{item?.relationshipInArrayPolyHasOne ? (
|
||||||
<div>
|
<div>
|
||||||
{typeof item?.relationshipInArrayPolyHasOne.value === 'string'
|
{typeof item?.relationshipInArrayPolyHasOne.value === 'string'
|
||||||
? item?.relationshipInArrayPolyHasOne.value
|
? item?.relationshipInArrayPolyHasOne.value
|
||||||
: item?.relationshipInArrayPolyHasOne.value.title}
|
: item?.relationshipInArrayPolyHasOne.value.title}
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>None</div>
|
||||||
)}
|
)}
|
||||||
<p>Polymorphic Has Many:</p>
|
<p>
|
||||||
{item?.relationshipInArrayPolyHasMany?.map(
|
<b>Polymorphic Has Many:</b>
|
||||||
(rel, relIndex) =>
|
</p>
|
||||||
rel.value && (
|
{item?.relationshipInArrayPolyHasMany?.map((rel, relIndex) =>
|
||||||
<div key={relIndex}>
|
rel.value ? (
|
||||||
{typeof rel.value === 'string' ? rel.value : rel.value.title}
|
<div key={relIndex}>
|
||||||
</div>
|
{typeof rel.value === 'string' ? rel.value : rel.value.title}
|
||||||
),
|
</div>
|
||||||
|
) : (
|
||||||
|
'null'
|
||||||
|
),
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -78,7 +78,9 @@ const serialize = (children?: Children): React.ReactNode[] =>
|
|||||||
case 'relationship':
|
case 'relationship':
|
||||||
return (
|
return (
|
||||||
<span key={i}>
|
<span key={i}>
|
||||||
{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}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
case 'link':
|
case 'link':
|
||||||
|
|||||||
Reference in New Issue
Block a user