fix(live-preview): populates rte uploads and relationships (#4379)
This commit is contained in:
@@ -53,13 +53,20 @@ export const traverseRichText = ({
|
||||
? Array.isArray(incomingData[key])
|
||||
? []
|
||||
: {}
|
||||
: incomingData[key]
|
||||
: undefined
|
||||
}
|
||||
|
||||
const isRelationship = key === 'value' && 'relationTo' in incomingData
|
||||
|
||||
if (isRelationship) {
|
||||
const needsPopulation = !result.value || typeof result.value !== 'object'
|
||||
// or if there are no keys besides id
|
||||
const needsPopulation =
|
||||
!result.value ||
|
||||
typeof result.value !== 'object' ||
|
||||
(typeof result.value === 'object' &&
|
||||
Object.keys(result.value).length === 1 &&
|
||||
'id' in result.value)
|
||||
|
||||
const hasChanged =
|
||||
result &&
|
||||
typeof result === 'object' &&
|
||||
@@ -71,7 +78,10 @@ export const traverseRichText = ({
|
||||
}
|
||||
|
||||
populationsByCollection[incomingData.relationTo].push({
|
||||
id: incomingData[key],
|
||||
id:
|
||||
incomingData[key] && typeof incomingData[key] === 'object'
|
||||
? incomingData[key].id
|
||||
: incomingData[key],
|
||||
accessor: 'value',
|
||||
ref: result,
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { CollectionConfig } from '../../../packages/payload/src/collections/config/types'
|
||||
|
||||
import { lexicalEditor } from '../../../packages/richtext-lexical/src'
|
||||
import { Archive } from '../blocks/ArchiveBlock'
|
||||
import { CallToAction } from '../blocks/CallToAction'
|
||||
import { Content } from '../blocks/Content'
|
||||
@@ -62,8 +63,15 @@ export const Pages: CollectionConfig = {
|
||||
label: 'Test',
|
||||
fields: [
|
||||
{
|
||||
name: 'relationshipInRichText',
|
||||
label: 'Rich Text — Slate',
|
||||
type: 'richText',
|
||||
name: 'richTextSlate',
|
||||
},
|
||||
{
|
||||
label: 'Rich Text — Lexical',
|
||||
type: 'richText',
|
||||
name: 'richTextLexical',
|
||||
editor: lexicalEditor({}),
|
||||
},
|
||||
{
|
||||
name: 'relationshipAsUpload',
|
||||
|
||||
@@ -154,10 +154,7 @@ describe('Collections - Live Preview', () => {
|
||||
expect(mergedData._numberOfRequests).toEqual(0)
|
||||
})
|
||||
|
||||
// TODO: this test is not working in Postgres
|
||||
// This is because of how relationships are handled in `mergeData`
|
||||
// This test passes in MongoDB, though
|
||||
it.skip('— uploads - adds and removes media', async () => {
|
||||
it('— uploads - adds and removes media', async () => {
|
||||
const initialData: Partial<Page> = {
|
||||
title: 'Test Page',
|
||||
}
|
||||
@@ -198,6 +195,166 @@ describe('Collections - Live Preview', () => {
|
||||
|
||||
expect(mergedDataWithoutUpload.hero.media).toBeFalsy()
|
||||
})
|
||||
|
||||
it('— uploads - populates within Slate rich text editor', async () => {
|
||||
const initialData: Partial<Page> = {
|
||||
title: 'Test Page',
|
||||
}
|
||||
|
||||
// Add upload
|
||||
const merge1 = await mergeData({
|
||||
depth: 1,
|
||||
fieldSchema: schemaJSON,
|
||||
incomingData: {
|
||||
...initialData,
|
||||
richTextSlate: [
|
||||
{
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
value: media.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
initialData,
|
||||
serverURL,
|
||||
returnNumberOfRequests: true,
|
||||
})
|
||||
|
||||
expect(merge1.richTextSlate).toHaveLength(1)
|
||||
expect(merge1.richTextSlate[0].value).toMatchObject(media)
|
||||
expect(merge1._numberOfRequests).toEqual(1)
|
||||
|
||||
// Remove upload
|
||||
const merge2 = await mergeData({
|
||||
depth: 1,
|
||||
fieldSchema: schemaJSON,
|
||||
incomingData: {
|
||||
...merge1,
|
||||
richTextSlate: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
text: 'Hello, world!',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
initialData,
|
||||
serverURL,
|
||||
returnNumberOfRequests: true,
|
||||
})
|
||||
|
||||
expect(merge2.richTextSlate).toHaveLength(1)
|
||||
expect(merge2.richTextSlate[0].value).toBeFalsy()
|
||||
expect(merge2.richTextSlate[0].type).toEqual('paragraph')
|
||||
expect(merge2._numberOfRequests).toEqual(0)
|
||||
})
|
||||
|
||||
it('— uploads - populates within Lexical rich text editor', async () => {
|
||||
const initialData: Partial<Page> = {
|
||||
title: 'Test Page',
|
||||
}
|
||||
|
||||
// Add upload
|
||||
const merge1 = await mergeData({
|
||||
depth: 1,
|
||||
fieldSchema: schemaJSON,
|
||||
incomingData: {
|
||||
...initialData,
|
||||
richTextLexical: {
|
||||
root: {
|
||||
type: 'root',
|
||||
format: '',
|
||||
indent: 0,
|
||||
version: 1,
|
||||
children: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'Hello, world!',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'paragraph',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
format: '',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
version: 1,
|
||||
value: media.id,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
},
|
||||
},
|
||||
},
|
||||
initialData,
|
||||
serverURL,
|
||||
returnNumberOfRequests: true,
|
||||
})
|
||||
|
||||
expect(merge1.richTextLexical.root.children).toHaveLength(2)
|
||||
expect(merge1.richTextLexical.root.children[1].value).toMatchObject(media)
|
||||
expect(merge1._numberOfRequests).toEqual(1)
|
||||
|
||||
// Remove upload
|
||||
const merge2 = await mergeData({
|
||||
depth: 1,
|
||||
fieldSchema: schemaJSON,
|
||||
incomingData: {
|
||||
...merge1,
|
||||
richTextLexical: {
|
||||
root: {
|
||||
type: 'root',
|
||||
format: '',
|
||||
indent: 0,
|
||||
version: 1,
|
||||
children: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
detail: 0,
|
||||
format: 0,
|
||||
mode: 'normal',
|
||||
style: '',
|
||||
text: 'Hello, world!',
|
||||
type: 'text',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'paragraph',
|
||||
version: 1,
|
||||
},
|
||||
],
|
||||
direction: 'ltr',
|
||||
},
|
||||
},
|
||||
},
|
||||
initialData,
|
||||
serverURL,
|
||||
returnNumberOfRequests: true,
|
||||
})
|
||||
|
||||
expect(merge2.richTextLexical.root.children).toHaveLength(1)
|
||||
expect(merge2.richTextLexical.root.children[0].value).toBeFalsy()
|
||||
expect(merge2.richTextLexical.root.children[0].type).toEqual('paragraph')
|
||||
})
|
||||
|
||||
it('— relationships - populates monomorphic has one relationships', async () => {
|
||||
const initialData: Partial<Page> = {
|
||||
title: 'Test Page',
|
||||
@@ -422,7 +579,7 @@ describe('Collections - Live Preview', () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
initialData,
|
||||
initialData: merge1,
|
||||
serverURL,
|
||||
returnNumberOfRequests: true,
|
||||
})
|
||||
@@ -451,7 +608,130 @@ describe('Collections - Live Preview', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('— relationships - populates within rich text', async () => {
|
||||
it('— relationships - populates within Slate rich text editor', async () => {
|
||||
const initialData: Partial<Page> = {
|
||||
title: 'Test Page',
|
||||
}
|
||||
|
||||
// Add a relationship and an upload
|
||||
const merge1 = await mergeData({
|
||||
depth: 1,
|
||||
fieldSchema: schemaJSON,
|
||||
incomingData: {
|
||||
...initialData,
|
||||
richTextSlate: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
text: ' ',
|
||||
},
|
||||
],
|
||||
relationTo: 'posts',
|
||||
type: 'relationship',
|
||||
value: {
|
||||
id: testPost.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
text: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
text: '',
|
||||
},
|
||||
],
|
||||
relationTo: 'media',
|
||||
type: 'upload',
|
||||
value: {
|
||||
id: media.id,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
initialData,
|
||||
serverURL,
|
||||
returnNumberOfRequests: true,
|
||||
})
|
||||
|
||||
expect(merge1._numberOfRequests).toEqual(2)
|
||||
expect(merge1.richTextSlate).toHaveLength(3)
|
||||
expect(merge1.richTextSlate[0].type).toEqual('relationship')
|
||||
expect(merge1.richTextSlate[0].value).toMatchObject(testPost)
|
||||
expect(merge1.richTextSlate[1].type).toEqual('paragraph')
|
||||
expect(merge1.richTextSlate[2].type).toEqual('upload')
|
||||
expect(merge1.richTextSlate[2].value).toMatchObject(media)
|
||||
|
||||
// Add a new node between the relationship and the upload
|
||||
const merge2 = await mergeData({
|
||||
depth: 1,
|
||||
fieldSchema: schemaJSON,
|
||||
incomingData: {
|
||||
...merge1,
|
||||
richTextSlate: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
text: ' ',
|
||||
},
|
||||
],
|
||||
relationTo: 'posts',
|
||||
type: 'relationship',
|
||||
value: {
|
||||
id: testPost.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
text: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
text: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
text: '',
|
||||
},
|
||||
],
|
||||
relationTo: 'media',
|
||||
type: 'upload',
|
||||
value: {
|
||||
id: media.id,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
initialData: merge1,
|
||||
serverURL,
|
||||
returnNumberOfRequests: true,
|
||||
})
|
||||
|
||||
expect(merge2._numberOfRequests).toEqual(1)
|
||||
expect(merge2.richTextSlate).toHaveLength(4)
|
||||
expect(merge2.richTextSlate[0].type).toEqual('relationship')
|
||||
expect(merge2.richTextSlate[0].value).toMatchObject(testPost)
|
||||
expect(merge2.richTextSlate[1].type).toEqual('paragraph')
|
||||
expect(merge2.richTextSlate[2].type).toEqual('paragraph')
|
||||
expect(merge2.richTextSlate[3].type).toEqual('upload')
|
||||
expect(merge2.richTextSlate[3].value).toMatchObject(media)
|
||||
})
|
||||
|
||||
it('— relationships - populates within Lexical rich text editor', async () => {
|
||||
const initialData: Partial<Page> = {
|
||||
title: 'Test Page',
|
||||
}
|
||||
@@ -462,56 +742,130 @@ describe('Collections - Live Preview', () => {
|
||||
fieldSchema: schemaJSON,
|
||||
incomingData: {
|
||||
...initialData,
|
||||
relationshipInRichText: [
|
||||
richTextLexical: {
|
||||
root: {
|
||||
type: 'root',
|
||||
format: '',
|
||||
indent: 0,
|
||||
version: 1,
|
||||
children: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
text: 'Paragraph 1',
|
||||
format: '',
|
||||
type: 'relationship',
|
||||
version: 1,
|
||||
relationTo: 'posts',
|
||||
value: {
|
||||
id: testPost.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'reference',
|
||||
reference: {
|
||||
relationTo: 'posts',
|
||||
value: testPost.id,
|
||||
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,
|
||||
},
|
||||
},
|
||||
},
|
||||
initialData,
|
||||
serverURL,
|
||||
returnNumberOfRequests: true,
|
||||
})
|
||||
|
||||
expect(merge1._numberOfRequests).toEqual(1)
|
||||
expect(merge1.relationshipInRichText).toHaveLength(2)
|
||||
expect(merge1.relationshipInRichText[1].reference.value).toMatchObject(testPost)
|
||||
expect(merge1._numberOfRequests).toEqual(2)
|
||||
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)
|
||||
|
||||
// Remove the relationship
|
||||
// Add a node before the populated one
|
||||
const merge2 = await mergeData({
|
||||
depth: 1,
|
||||
fieldSchema: schemaJSON,
|
||||
incomingData: {
|
||||
...merge1,
|
||||
relationshipInRichText: [
|
||||
richTextLexical: {
|
||||
root: {
|
||||
type: 'root',
|
||||
format: '',
|
||||
indent: 0,
|
||||
version: 1,
|
||||
children: [
|
||||
{
|
||||
format: '',
|
||||
type: 'relationship',
|
||||
version: 1,
|
||||
relationTo: 'posts',
|
||||
value: {
|
||||
id: testPost.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
children: [],
|
||||
direction: null,
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'paragraph',
|
||||
text: 'Paragraph 1',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
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,
|
||||
},
|
||||
initialData,
|
||||
},
|
||||
},
|
||||
initialData: merge1,
|
||||
serverURL,
|
||||
returnNumberOfRequests: true,
|
||||
})
|
||||
|
||||
expect(merge2._numberOfRequests).toEqual(0)
|
||||
expect(merge2.relationshipInRichText).toHaveLength(1)
|
||||
expect(merge2.relationshipInRichText[0].type).toEqual('paragraph')
|
||||
expect(merge2._numberOfRequests).toEqual(1)
|
||||
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)
|
||||
})
|
||||
|
||||
it('— relationships - does not re-populate existing rich text relationships', async () => {
|
||||
const initialData: Partial<Page> = {
|
||||
title: 'Test Page',
|
||||
relationshipInRichText: [
|
||||
richTextSlate: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
text: 'Paragraph 1',
|
||||
@@ -532,7 +886,7 @@ describe('Collections - Live Preview', () => {
|
||||
fieldSchema: schemaJSON,
|
||||
incomingData: {
|
||||
...initialData,
|
||||
relationshipInRichText: [
|
||||
richTextSlate: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
text: 'Paragraph 1 (Updated)',
|
||||
@@ -552,9 +906,9 @@ describe('Collections - Live Preview', () => {
|
||||
})
|
||||
|
||||
expect(merge1._numberOfRequests).toEqual(0)
|
||||
expect(merge1.relationshipInRichText).toHaveLength(2)
|
||||
expect(merge1.relationshipInRichText[0].text).toEqual('Paragraph 1 (Updated)')
|
||||
expect(merge1.relationshipInRichText[1].reference.value).toMatchObject(testPost)
|
||||
expect(merge1.richTextSlate).toHaveLength(2)
|
||||
expect(merge1.richTextSlate[0].text).toEqual('Paragraph 1 (Updated)')
|
||||
expect(merge1.richTextSlate[1].reference.value).toMatchObject(testPost)
|
||||
})
|
||||
|
||||
it('— relationships - populates within blocks', async () => {
|
||||
|
||||
@@ -6,7 +6,6 @@ import React from 'react'
|
||||
import { PAYLOAD_SERVER_URL } from '@/app/_api/serverURL'
|
||||
import { Hero } from '@/app/_components/Hero'
|
||||
import { Blocks } from '@/app/_components/Blocks'
|
||||
import { RelationshipsBlock } from '@/app/_blocks/Relationships'
|
||||
|
||||
export const PageClient: React.FC<{
|
||||
page: PageType
|
||||
|
||||
@@ -22,9 +22,15 @@ export const RelationshipsBlock: React.FC<RelationshipsBlockProps> = (props) =>
|
||||
This block is for testing purposes only. It renders every possible type of relationship.
|
||||
</p>
|
||||
<p>
|
||||
<b>Rich Text:</b>
|
||||
<b>Rich Text — Slate:</b>
|
||||
</p>
|
||||
{data?.relationshipInRichText && <RichText content={data.relationshipInRichText} />}
|
||||
{data?.richTextSlate && <RichText content={data.richTextSlate} renderUploadFilenameOnly />}
|
||||
<p>
|
||||
<b>Rich Text — Lexical:</b>
|
||||
</p>
|
||||
{data?.richTextLexical && (
|
||||
<RichText serializer="lexical" content={data.richTextLexical} renderUploadFilenameOnly />
|
||||
)}
|
||||
<p>
|
||||
<b>Upload:</b>
|
||||
</p>
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
import React from 'react'
|
||||
|
||||
import serialize from './serialize'
|
||||
import serializeSlate from './serializeSlate'
|
||||
import serializeLexical from './serializeLexical'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
const RichText: React.FC<{ className?: string; content: any }> = ({ className, content }) => {
|
||||
const RichText: React.FC<{
|
||||
className?: string
|
||||
content: any
|
||||
renderUploadFilenameOnly?: boolean
|
||||
serializer?: 'lexical' | 'slate'
|
||||
}> = ({ className, content, renderUploadFilenameOnly, serializer = 'slate' }) => {
|
||||
if (!content) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={[classes.richText, className].filter(Boolean).join(' ')}>
|
||||
{serialize(content)}
|
||||
{serializer === 'slate'
|
||||
? serializeSlate(content, renderUploadFilenameOnly)
|
||||
: serializeLexical(content, renderUploadFilenameOnly)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import type { SerializedEditorState } from 'lexical'
|
||||
import { CMSLink } from '../Link'
|
||||
import { Media } from '../Media'
|
||||
|
||||
const serializer = (
|
||||
content?: SerializedEditorState['root']['children'],
|
||||
renderUploadFilenameOnly?: boolean,
|
||||
): React.ReactNode | React.ReactNode[] =>
|
||||
content?.map((node, i) => {
|
||||
switch (node.type) {
|
||||
case 'h1':
|
||||
return <h1 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h1>
|
||||
|
||||
case 'h2':
|
||||
return <h2 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h2>
|
||||
|
||||
case 'h3':
|
||||
return <h3 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h3>
|
||||
|
||||
case 'h4':
|
||||
return <h4 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h4>
|
||||
|
||||
case 'h5':
|
||||
return <h5 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h5>
|
||||
|
||||
case 'h6':
|
||||
return <h6 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h6>
|
||||
|
||||
case 'quote':
|
||||
return (
|
||||
<blockquote key={i}>
|
||||
{serializeLexical(node?.children, renderUploadFilenameOnly)}
|
||||
</blockquote>
|
||||
)
|
||||
|
||||
case 'ul':
|
||||
return <ul key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</ul>
|
||||
|
||||
case 'ol':
|
||||
return <ol key={i}>{serializeLexical(node.children, renderUploadFilenameOnly)}</ol>
|
||||
|
||||
case 'li':
|
||||
return <li key={i}>{serializeLexical(node.children, renderUploadFilenameOnly)}</li>
|
||||
|
||||
case 'relationship':
|
||||
return (
|
||||
<span key={i}>
|
||||
{node.value && typeof node.value === 'object'
|
||||
? node.value.title || node.value.id
|
||||
: node.value}
|
||||
</span>
|
||||
)
|
||||
|
||||
case 'link':
|
||||
return (
|
||||
<CMSLink
|
||||
key={i}
|
||||
type={node.linkType === 'internal' ? 'reference' : 'custom'}
|
||||
url={node.url}
|
||||
reference={node.doc as any}
|
||||
newTab={Boolean(node?.newTab)}
|
||||
>
|
||||
{serializer(node?.children, renderUploadFilenameOnly)}
|
||||
</CMSLink>
|
||||
)
|
||||
|
||||
case 'upload':
|
||||
if (renderUploadFilenameOnly) {
|
||||
return <span key={i}>{node.value.filename}</span>
|
||||
}
|
||||
|
||||
return <Media key={i} resource={node?.value} />
|
||||
|
||||
case 'paragraph':
|
||||
return <p key={i}>{serializer(node?.children, renderUploadFilenameOnly)}</p>
|
||||
|
||||
case 'text':
|
||||
return <span key={i}>{node.text}</span>
|
||||
}
|
||||
})
|
||||
|
||||
const serializeLexical = (
|
||||
content?: SerializedEditorState,
|
||||
renderUploadFilenameOnly?: boolean,
|
||||
): React.ReactNode | React.ReactNode[] => {
|
||||
return serializer(content?.root?.children, renderUploadFilenameOnly)
|
||||
}
|
||||
|
||||
export default serializeLexical
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import escapeHTML from 'escape-html'
|
||||
import Link from 'next/link'
|
||||
import { Text } from 'slate'
|
||||
import { CMSLink } from '../Link'
|
||||
import { Media } from '../Media'
|
||||
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
type Children = Leaf[]
|
||||
@@ -15,7 +15,10 @@ type Leaf = {
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
const serialize = (children?: Children): React.ReactNode[] =>
|
||||
const serializeSlate = (
|
||||
children?: Children,
|
||||
renderUploadFilenameOnly?: boolean,
|
||||
): React.ReactNode[] =>
|
||||
children?.map((node, i) => {
|
||||
if (Text.isText(node)) {
|
||||
let text = <span dangerouslySetInnerHTML={{ __html: escapeHTML(node.text) }} />
|
||||
@@ -57,25 +60,39 @@ const serialize = (children?: Children): React.ReactNode[] =>
|
||||
|
||||
switch (node.type) {
|
||||
case 'h1':
|
||||
return <h1 key={i}>{serialize(node?.children)}</h1>
|
||||
return <h1 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h1>
|
||||
|
||||
case 'h2':
|
||||
return <h2 key={i}>{serialize(node?.children)}</h2>
|
||||
return <h2 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h2>
|
||||
|
||||
case 'h3':
|
||||
return <h3 key={i}>{serialize(node?.children)}</h3>
|
||||
return <h3 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h3>
|
||||
|
||||
case 'h4':
|
||||
return <h4 key={i}>{serialize(node?.children)}</h4>
|
||||
return <h4 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h4>
|
||||
|
||||
case 'h5':
|
||||
return <h5 key={i}>{serialize(node?.children)}</h5>
|
||||
return <h5 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h5>
|
||||
|
||||
case 'h6':
|
||||
return <h6 key={i}>{serialize(node?.children)}</h6>
|
||||
return <h6 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h6>
|
||||
|
||||
case 'quote':
|
||||
return <blockquote key={i}>{serialize(node?.children)}</blockquote>
|
||||
return (
|
||||
<blockquote key={i}>
|
||||
{serializeSlate(node?.children, renderUploadFilenameOnly)}
|
||||
</blockquote>
|
||||
)
|
||||
|
||||
case 'ul':
|
||||
return <ul key={i}>{serialize(node?.children)}</ul>
|
||||
return <ul key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</ul>
|
||||
|
||||
case 'ol':
|
||||
return <ol key={i}>{serialize(node.children)}</ol>
|
||||
return <ol key={i}>{serializeSlate(node.children, renderUploadFilenameOnly)}</ol>
|
||||
|
||||
case 'li':
|
||||
return <li key={i}>{serialize(node.children)}</li>
|
||||
return <li key={i}>{serializeSlate(node.children, renderUploadFilenameOnly)}</li>
|
||||
|
||||
case 'relationship':
|
||||
return (
|
||||
<span key={i}>
|
||||
@@ -84,6 +101,7 @@ const serialize = (children?: Children): React.ReactNode[] =>
|
||||
: node.value}
|
||||
</span>
|
||||
)
|
||||
|
||||
case 'link':
|
||||
return (
|
||||
<CMSLink
|
||||
@@ -93,13 +111,20 @@ const serialize = (children?: Children): React.ReactNode[] =>
|
||||
reference={node.doc as any}
|
||||
newTab={Boolean(node?.newTab)}
|
||||
>
|
||||
{serialize(node?.children)}
|
||||
{serializeSlate(node?.children, renderUploadFilenameOnly)}
|
||||
</CMSLink>
|
||||
)
|
||||
|
||||
case 'upload':
|
||||
if (renderUploadFilenameOnly) {
|
||||
return <span key={i}>{node.value.filename}</span>
|
||||
}
|
||||
|
||||
return <Media key={i} resource={node?.value} />
|
||||
|
||||
default:
|
||||
return <p key={i}>{serialize(node?.children)}</p>
|
||||
return <p key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</p>
|
||||
}
|
||||
}) || []
|
||||
|
||||
export default serialize
|
||||
export default serializeSlate
|
||||
@@ -11,6 +11,7 @@ export interface Config {
|
||||
users: User
|
||||
pages: Page
|
||||
posts: Post
|
||||
tenants: Tenant
|
||||
categories: Category
|
||||
media: Media
|
||||
'payload-preferences': PayloadPreference
|
||||
@@ -37,6 +38,7 @@ export interface User {
|
||||
export interface Page {
|
||||
id: string
|
||||
slug: string
|
||||
tenant?: (string | null) | Tenant
|
||||
title: string
|
||||
hero: {
|
||||
type: 'none' | 'highImpact' | 'lowImpact'
|
||||
@@ -152,16 +154,6 @@ export interface Page {
|
||||
}
|
||||
)[]
|
||||
| null
|
||||
meta?: {
|
||||
title?: string | null
|
||||
description?: string | null
|
||||
image?: string | Media | null
|
||||
}
|
||||
relationshipInRichText?:
|
||||
| {
|
||||
[k: string]: unknown
|
||||
}[]
|
||||
| null
|
||||
relationshipAsUpload?: string | Media | null
|
||||
relationshipMonoHasOne?: (string | null) | Post
|
||||
relationshipMonoHasMany?: (string | Post)[] | null
|
||||
@@ -198,6 +190,41 @@ export interface Page {
|
||||
id?: string | null
|
||||
}[]
|
||||
| null
|
||||
richTextSlate?:
|
||||
| {
|
||||
[k: string]: unknown
|
||||
}[]
|
||||
| null
|
||||
richTextLexical?: {
|
||||
root: {
|
||||
children: {
|
||||
type: string
|
||||
version: number
|
||||
[k: string]: unknown
|
||||
}[]
|
||||
direction: ('ltr' | 'rtl') | null
|
||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''
|
||||
indent: number
|
||||
type: string
|
||||
version: number
|
||||
}
|
||||
[k: string]: unknown
|
||||
} | null
|
||||
tab: {
|
||||
relationshipInTab?: (string | null) | Post
|
||||
}
|
||||
meta?: {
|
||||
title?: string | null
|
||||
description?: string | null
|
||||
image?: string | Media | null
|
||||
}
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
export interface Tenant {
|
||||
id: string
|
||||
title: string
|
||||
clientURL: string
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
@@ -221,6 +248,7 @@ export interface Media {
|
||||
export interface Post {
|
||||
id: string
|
||||
slug: string
|
||||
tenant?: (string | null) | Tenant
|
||||
title: string
|
||||
hero: {
|
||||
type: 'none' | 'highImpact' | 'lowImpact'
|
||||
|
||||
@@ -154,11 +154,6 @@ export interface Page {
|
||||
}
|
||||
)[]
|
||||
| null
|
||||
relationshipInRichText?:
|
||||
| {
|
||||
[k: string]: unknown
|
||||
}[]
|
||||
| null
|
||||
relationshipAsUpload?: string | Media | null
|
||||
relationshipMonoHasOne?: (string | null) | Post
|
||||
relationshipMonoHasMany?: (string | Post)[] | null
|
||||
@@ -195,6 +190,26 @@ export interface Page {
|
||||
id?: string | null
|
||||
}[]
|
||||
| null
|
||||
richTextSlate?:
|
||||
| {
|
||||
[k: string]: unknown
|
||||
}[]
|
||||
| null
|
||||
richTextLexical?: {
|
||||
root: {
|
||||
children: {
|
||||
type: string
|
||||
version: number
|
||||
[k: string]: unknown
|
||||
}[]
|
||||
direction: ('ltr' | 'rtl') | null
|
||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''
|
||||
indent: number
|
||||
type: string
|
||||
version: number
|
||||
}
|
||||
[k: string]: unknown
|
||||
} | null
|
||||
tab: {
|
||||
relationshipInTab?: (string | null) | Post
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ export const home: Omit<Page, 'createdAt' | 'id' | 'updatedAt'> = {
|
||||
},
|
||||
],
|
||||
relationshipAsUpload: '{{MEDIA_ID}}',
|
||||
relationshipInRichText: [
|
||||
richTextSlate: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
@@ -108,7 +108,65 @@ export const home: Omit<Page, 'createdAt' | 'id' | 'updatedAt'> = {
|
||||
id: '{{POST_1_ID}}',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
text: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
children: [
|
||||
{
|
||||
text: '',
|
||||
},
|
||||
],
|
||||
relationTo: 'media',
|
||||
type: 'upload',
|
||||
value: {
|
||||
id: '{{MEDIA_ID}}',
|
||||
},
|
||||
},
|
||||
],
|
||||
richTextLexical: {
|
||||
root: {
|
||||
type: 'root',
|
||||
format: '',
|
||||
indent: 0,
|
||||
version: 1,
|
||||
children: [
|
||||
{
|
||||
format: '',
|
||||
type: 'relationship',
|
||||
version: 1,
|
||||
relationTo: 'posts',
|
||||
value: {
|
||||
id: '{{POST_1_ID}}',
|
||||
},
|
||||
},
|
||||
{
|
||||
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}}' }],
|
||||
|
||||
Reference in New Issue
Block a user