fix: admin.hidden not respected for RSCs, render-field server function not respecting field-overrides client-side (#13869)

This PR fixes 2 issues:

- the `fieldConfig.admin.hidden` property had no effect for react server
components, because the RSC was returned before we're checking for
`admin.hidden` in RenderFields
- the `render-field` server function did not propagate fieldConfig
overrides to the clientProps. This means overriding `admin.Label` had no
effect

Adds e2e tests for both issues
This commit is contained in:
Alessio Gravili
2025-09-22 11:36:41 -07:00
committed by GitHub
parent c33c037e55
commit 228e8f281a
8 changed files with 114 additions and 63 deletions

View File

@@ -2,6 +2,7 @@ import { expect, test } from '@playwright/test'
import { AdminUrlUtil } from 'helpers/adminUrlUtil.js'
import { reInitializeDB } from 'helpers/reInitializeDB.js'
import path from 'path'
import { wait } from 'payload/shared'
import { fileURLToPath } from 'url'
import { ensureCompilationIsDone, saveDocAndAssert } from '../../../helpers.js'
@@ -102,5 +103,19 @@ describe('Lexical On Demand', () => {
await expect(lexical.drawer.locator('#field-rows')).toHaveValue('5')
await expect(lexical.drawer.locator('#field-columns')).toHaveValue('5')
})
test('on-demand editor renders label', async ({ page }) => {
await expect(page.locator('.field-label[for="field-myField"]')).toHaveText('My Label')
})
test('ensure anchor richText field is hidden', async ({ page }) => {
// Important: Wait for all fields to render
await wait(1000)
await expect(page.locator('.shimmer')).toHaveCount(0)
await expect(page.locator('.field-label[for="field-hiddenAnchor"]')).toHaveCount(0)
await expect(page.locator('.rich-text-lexical')).toHaveCount(1)
})
})
})

View File

@@ -6,8 +6,6 @@ import type { JSONFieldClientComponent } from 'payload'
import { buildEditorState, RenderLexical } from '@payloadcms/richtext-lexical/client'
import React, { useState } from 'react'
import { lexicalFullyFeaturedSlug } from '../../slugs.js'
export const Component: JSONFieldClientComponent = () => {
const [value, setValue] = useState<DefaultTypedEditorState | undefined>(() =>
buildEditorState({ text: 'state default' }),
@@ -21,9 +19,9 @@ export const Component: JSONFieldClientComponent = () => {
<div>
Default Component:
<RenderLexical
field={{ name: 'myField' }}
field={{ name: 'myField', label: 'My Label' }}
initialValue={buildEditorState({ text: 'defaultValue' })}
schemaPath={`collection.${lexicalFullyFeaturedSlug}.richText`}
schemaPath={`collection.OnDemandOutsideForm.hiddenAnchor`}
setValue={setValue as any}
value={value}
/>

View File

@@ -1,5 +1,7 @@
import type { CollectionConfig } from 'payload'
import { EXPERIMENTAL_TableFeature, lexicalEditor } from '@payloadcms/richtext-lexical'
export const OnDemandOutsideForm: CollectionConfig = {
slug: 'OnDemandOutsideForm',
fields: [
@@ -12,5 +14,15 @@ export const OnDemandOutsideForm: CollectionConfig = {
},
},
},
{
name: 'hiddenAnchor',
type: 'richText',
editor: lexicalEditor({
features: ({ rootFeatures }) => [...rootFeatures, EXPERIMENTAL_TableFeature()],
}),
admin: {
hidden: true,
},
},
],
}

View File

@@ -130,7 +130,7 @@ export interface Config {
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
};
db: {
defaultIDType: string;
defaultIDType: number;
};
globals: {
tabsWithRichText: TabsWithRichText;
@@ -170,7 +170,7 @@ export interface UserAuthOperations {
* via the `definition` "lexical-fully-featured".
*/
export interface LexicalFullyFeatured {
id: string;
id: number;
richText?: {
root: {
type: string;
@@ -194,7 +194,7 @@ export interface LexicalFullyFeatured {
* via the `definition` "lexical-link-feature".
*/
export interface LexicalLinkFeature {
id: string;
id: number;
richText?: {
root: {
type: string;
@@ -242,7 +242,7 @@ export interface LexicalHeadingFeature {
* via the `definition` "lexical-jsx-converter".
*/
export interface LexicalJsxConverter {
id: string;
id: number;
richText?: {
root: {
type: string;
@@ -266,7 +266,7 @@ export interface LexicalJsxConverter {
* via the `definition` "lexical-fields".
*/
export interface LexicalField {
id: string;
id: number;
title: string;
lexicalRootEditor?: {
root: {
@@ -322,7 +322,7 @@ export interface LexicalField {
* via the `definition` "lexical-migrate-fields".
*/
export interface LexicalMigrateField {
id: string;
id: number;
title: string;
lexicalWithLexicalPluginData?: {
root: {
@@ -417,7 +417,7 @@ export interface LexicalMigrateField {
* via the `definition` "lexical-localized-fields".
*/
export interface LexicalLocalizedField {
id: string;
id: number;
title: string;
/**
* Non-localized field with localized block subfields
@@ -463,7 +463,7 @@ export interface LexicalLocalizedField {
* via the `definition` "lexicalObjectReferenceBug".
*/
export interface LexicalObjectReferenceBug {
id: string;
id: number;
lexicalDefault?: {
root: {
type: string;
@@ -502,7 +502,7 @@ export interface LexicalObjectReferenceBug {
* via the `definition` "LexicalInBlock".
*/
export interface LexicalInBlock {
id: string;
id: number;
content?: {
root: {
type: string;
@@ -548,7 +548,7 @@ export interface LexicalInBlock {
* via the `definition` "lexical-access-control".
*/
export interface LexicalAccessControl {
id: string;
id: number;
title?: string | null;
richText?: {
root: {
@@ -573,7 +573,7 @@ export interface LexicalAccessControl {
* via the `definition` "lexical-relationship-fields".
*/
export interface LexicalRelationshipField {
id: string;
id: number;
richText?: {
root: {
type: string;
@@ -612,7 +612,7 @@ export interface LexicalRelationshipField {
* via the `definition` "rich-text-fields".
*/
export interface RichTextField {
id: string;
id: number;
title: string;
lexicalCustomFields: {
root: {
@@ -693,7 +693,7 @@ export interface RichTextField {
* via the `definition` "text-fields".
*/
export interface TextField {
id: string;
id: number;
text: string;
hiddenTextField?: string | null;
/**
@@ -745,9 +745,9 @@ export interface TextField {
* via the `definition` "uploads".
*/
export interface Upload {
id: string;
id: number;
text?: string | null;
media?: (string | null) | Upload;
media?: (number | null) | Upload;
updatedAt: string;
createdAt: string;
url?: string | null;
@@ -765,7 +765,7 @@ export interface Upload {
* via the `definition` "array-fields".
*/
export interface ArrayField {
id: string;
id: number;
title?: string | null;
items: {
text: string;
@@ -863,7 +863,7 @@ export interface ArrayField {
* via the `definition` "OnDemandForm".
*/
export interface OnDemandForm {
id: string;
id: number;
json?:
| {
[k: string]: unknown;
@@ -881,7 +881,7 @@ export interface OnDemandForm {
* via the `definition` "OnDemandOutsideForm".
*/
export interface OnDemandOutsideForm {
id: string;
id: number;
json?:
| {
[k: string]: unknown;
@@ -891,6 +891,21 @@ export interface OnDemandOutsideForm {
| number
| boolean
| null;
hiddenAnchor?: {
root: {
type: string;
children: {
type: any;
version: number;
[k: string]: unknown;
}[];
direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number;
version: number;
};
[k: string]: unknown;
} | null;
updatedAt: string;
createdAt: string;
}
@@ -899,7 +914,7 @@ export interface OnDemandOutsideForm {
* via the `definition` "users".
*/
export interface User {
id: string;
id: number;
updatedAt: string;
createdAt: string;
email: string;
@@ -923,15 +938,15 @@ export interface User {
* via the `definition` "payload-locked-documents".
*/
export interface PayloadLockedDocument {
id: string;
id: number;
document?:
| ({
relationTo: 'lexical-fully-featured';
value: string | LexicalFullyFeatured;
value: number | LexicalFullyFeatured;
} | null)
| ({
relationTo: 'lexical-link-feature';
value: string | LexicalLinkFeature;
value: number | LexicalLinkFeature;
} | null)
| ({
relationTo: 'lexical-heading-feature';
@@ -939,68 +954,68 @@ export interface PayloadLockedDocument {
} | null)
| ({
relationTo: 'lexical-jsx-converter';
value: string | LexicalJsxConverter;
value: number | LexicalJsxConverter;
} | null)
| ({
relationTo: 'lexical-fields';
value: string | LexicalField;
value: number | LexicalField;
} | null)
| ({
relationTo: 'lexical-migrate-fields';
value: string | LexicalMigrateField;
value: number | LexicalMigrateField;
} | null)
| ({
relationTo: 'lexical-localized-fields';
value: string | LexicalLocalizedField;
value: number | LexicalLocalizedField;
} | null)
| ({
relationTo: 'lexicalObjectReferenceBug';
value: string | LexicalObjectReferenceBug;
value: number | LexicalObjectReferenceBug;
} | null)
| ({
relationTo: 'LexicalInBlock';
value: string | LexicalInBlock;
value: number | LexicalInBlock;
} | null)
| ({
relationTo: 'lexical-access-control';
value: string | LexicalAccessControl;
value: number | LexicalAccessControl;
} | null)
| ({
relationTo: 'lexical-relationship-fields';
value: string | LexicalRelationshipField;
value: number | LexicalRelationshipField;
} | null)
| ({
relationTo: 'rich-text-fields';
value: string | RichTextField;
value: number | RichTextField;
} | null)
| ({
relationTo: 'text-fields';
value: string | TextField;
value: number | TextField;
} | null)
| ({
relationTo: 'uploads';
value: string | Upload;
value: number | Upload;
} | null)
| ({
relationTo: 'array-fields';
value: string | ArrayField;
value: number | ArrayField;
} | null)
| ({
relationTo: 'OnDemandForm';
value: string | OnDemandForm;
value: number | OnDemandForm;
} | null)
| ({
relationTo: 'OnDemandOutsideForm';
value: string | OnDemandOutsideForm;
value: number | OnDemandOutsideForm;
} | null)
| ({
relationTo: 'users';
value: string | User;
value: number | User;
} | null);
globalSlug?: string | null;
user: {
relationTo: 'users';
value: string | User;
value: number | User;
};
updatedAt: string;
createdAt: string;
@@ -1010,10 +1025,10 @@ export interface PayloadLockedDocument {
* via the `definition` "payload-preferences".
*/
export interface PayloadPreference {
id: string;
id: number;
user: {
relationTo: 'users';
value: string | User;
value: number | User;
};
key?: string | null;
value?:
@@ -1033,7 +1048,7 @@ export interface PayloadPreference {
* via the `definition` "payload-migrations".
*/
export interface PayloadMigration {
id: string;
id: number;
name?: string | null;
batch?: number | null;
updatedAt: string;
@@ -1388,6 +1403,7 @@ export interface OnDemandFormSelect<T extends boolean = true> {
*/
export interface OnDemandOutsideFormSelect<T extends boolean = true> {
json?: T;
hiddenAnchor?: T;
updatedAt?: T;
createdAt?: T;
}
@@ -1450,7 +1466,7 @@ export interface PayloadMigrationsSelect<T extends boolean = true> {
* via the `definition` "tabsWithRichText".
*/
export interface TabsWithRichText {
id: string;
id: number;
tab1?: {
rt1?: {
root: {
@@ -1524,7 +1540,7 @@ export interface LexicalBlocksRadioButtonsBlock {
export interface AvatarGroupBlock {
avatars?:
| {
image?: (string | null) | Upload;
image?: (number | null) | Upload;
id?: string | null;
}[]
| null;