Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(richtext-*): ensure admin panel doesn't freeze with some field configurations consisting of 2+ richtext fields #8773

Merged
merged 3 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions packages/ui/src/providers/Config/createClientConfig/fields.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
LabelsClient,
MappedComponent,
Payload,
PayloadComponent,
RadioFieldClient,
RichTextFieldClient,
RichTextGenerateComponentMap,
Expand All @@ -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'
Expand Down Expand Up @@ -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',
Expand Down
8 changes: 8 additions & 0 deletions test/fields/collections/Lexical/e2e/main/e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
38 changes: 38 additions & 0 deletions test/fields/collections/LexicalObjectReferenceBug/index.ts
Original file line number Diff line number Diff line change
@@ -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',
},
],
},
},
}),
],
}),
},
],
}
2 changes: 2 additions & 0 deletions test/fields/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -46,6 +47,7 @@ export const collectionSlugs: CollectionConfig[] = [
LexicalFields,
LexicalMigrateFields,
LexicalLocalizedFields,
LexicalObjectReferenceBugCollection,
{
slug: 'users',
admin: {
Expand Down