fix(richtext-*): ensure admin panel doesn't freeze with some field configurations consisting of 2+ richtext fields (#8773)
See comments in code for proper explanation. In some cases, where 2 richtext `editor`s referencing the same `editor` are used, the admin panel will hang. That's because the server will send their client props that have the same object reference down to the client twice. Next.js sometimes does not like this and, ever since one of the v15 canaries, started to hang
This commit is contained in:
@@ -13,6 +13,7 @@ import type {
|
||||
LabelsClient,
|
||||
MappedComponent,
|
||||
Payload,
|
||||
PayloadComponent,
|
||||
RadioFieldClient,
|
||||
RichTextFieldClient,
|
||||
RichTextGenerateComponentMap,
|
||||
@@ -25,7 +26,7 @@ import type {
|
||||
TabsFieldClient,
|
||||
} from 'payload'
|
||||
|
||||
import { MissingEditorProp } from 'payload'
|
||||
import { deepCopyObjectSimple, MissingEditorProp } from 'payload'
|
||||
import { fieldAffectsData, fieldIsPresentationalOnly } from 'payload/shared'
|
||||
|
||||
import { getComponent } from './getComponent.js'
|
||||
@@ -247,15 +248,35 @@ export const createClientField = ({
|
||||
field.admin.components = {}
|
||||
}
|
||||
|
||||
/**
|
||||
* We have to deep copy all the props we send to the client (= FieldComponent.clientProps).
|
||||
* That way, every editor's field / cell props we send to the client have their own object references.
|
||||
*
|
||||
* If we send the same object reference to the client twice (e.g. through some configurations where 2 or more fields
|
||||
* reference the same editor object, like the root editor), the admin panel may hang indefinitely. This has been happening since
|
||||
* a newer Next.js update that made it break when sending the same object reference to the client twice.
|
||||
*
|
||||
* We can use deepCopyObjectSimple as client props should be JSON-serializable.
|
||||
*/
|
||||
const FieldComponent: PayloadComponent = incomingField.editor.FieldComponent
|
||||
if (typeof FieldComponent === 'object' && FieldComponent.clientProps) {
|
||||
FieldComponent.clientProps = deepCopyObjectSimple(FieldComponent.clientProps)
|
||||
}
|
||||
|
||||
field.admin.components.Field = createMappedComponent(
|
||||
incomingField.editor.FieldComponent,
|
||||
FieldComponent,
|
||||
serverProps,
|
||||
undefined,
|
||||
'incomingField.editor.FieldComponent',
|
||||
)
|
||||
|
||||
const CellComponent: PayloadComponent = incomingField.editor.CellComponent
|
||||
if (typeof CellComponent === 'object' && CellComponent.clientProps) {
|
||||
CellComponent.clientProps = deepCopyObjectSimple(CellComponent.clientProps)
|
||||
}
|
||||
|
||||
field.admin.components.Cell = createMappedComponent(
|
||||
incomingField.editor.CellComponent,
|
||||
CellComponent,
|
||||
serverProps,
|
||||
undefined,
|
||||
'incomingField.editor.CellComponent',
|
||||
|
||||
@@ -553,6 +553,14 @@ describe('lexicalMain', () => {
|
||||
await expect(relationshipListDrawer).toHaveText('Array Fields')
|
||||
})
|
||||
|
||||
test('ensure navigation to collection that used to cause admin panel freeze due to object references bug is possible', async () => {
|
||||
const url: AdminUrlUtil = new AdminUrlUtil(serverURL, 'lexicalObjectReferenceBug')
|
||||
await page.goto(url.create)
|
||||
|
||||
await expect(page.locator('.rich-text-lexical').nth(0)).toBeVisible()
|
||||
await expect(page.locator('.rich-text-lexical').nth(1)).toBeVisible()
|
||||
})
|
||||
|
||||
describe('localization', () => {
|
||||
test.skip('ensure simple localized lexical field works', async () => {
|
||||
await navigateToLexicalFields(true, true)
|
||||
|
||||
38
test/fields/collections/LexicalObjectReferenceBug/index.ts
Normal file
38
test/fields/collections/LexicalObjectReferenceBug/index.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
import { lexicalEditor, UploadFeature } from '@payloadcms/richtext-lexical'
|
||||
|
||||
/**
|
||||
* Do not change this specific CollectionConfig. Simply having this config in payload used to cause the admin panel to hang.
|
||||
* Thus, simply having this config in the test suite is enough to test the bug fix and prevent regressions. In case of regression,
|
||||
* the entire admin panel will hang again and all tests will fail.
|
||||
*/
|
||||
export const LexicalObjectReferenceBugCollection: CollectionConfig = {
|
||||
slug: 'lexicalObjectReferenceBug',
|
||||
fields: [
|
||||
{
|
||||
name: 'lexicalDefault',
|
||||
type: 'richText',
|
||||
},
|
||||
{
|
||||
name: 'lexicalEditor',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({
|
||||
features: [
|
||||
UploadFeature({
|
||||
collections: {
|
||||
media: {
|
||||
fields: [
|
||||
{
|
||||
name: 'caption',
|
||||
type: 'richText',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import JSONFields from './collections/JSON/index.js'
|
||||
import { LexicalFields } from './collections/Lexical/index.js'
|
||||
import { LexicalLocalizedFields } from './collections/LexicalLocalized/index.js'
|
||||
import { LexicalMigrateFields } from './collections/LexicalMigrate/index.js'
|
||||
import { LexicalObjectReferenceBugCollection } from './collections/LexicalObjectReferenceBug/index.js'
|
||||
import { LexicalRelationshipsFields } from './collections/LexicalRelationships/index.js'
|
||||
import NumberFields from './collections/Number/index.js'
|
||||
import PointFields from './collections/Point/index.js'
|
||||
@@ -46,6 +47,7 @@ export const collectionSlugs: CollectionConfig[] = [
|
||||
LexicalFields,
|
||||
LexicalMigrateFields,
|
||||
LexicalLocalizedFields,
|
||||
LexicalObjectReferenceBugCollection,
|
||||
{
|
||||
slug: 'users',
|
||||
admin: {
|
||||
|
||||
Reference in New Issue
Block a user