From 174bde34c5956bf9b46f358771d1bceb1bf3db8c Mon Sep 17 00:00:00 2001 From: James Date: Fri, 15 Nov 2024 17:18:37 -0500 Subject: [PATCH] fix: lexical was incorrectly set to readonly in blocks --- .../addFieldStatePromise.ts | 15 +++-- .../collections/RichText/index.ts | 22 ++++++++ test/access-control/config.ts | 2 + test/access-control/e2e.spec.ts | 24 ++++++++ test/access-control/payload-types.ts | 56 +++++++++++++++++++ 5 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 test/access-control/collections/RichText/index.ts diff --git a/packages/ui/src/forms/fieldSchemasToFormState/addFieldStatePromise.ts b/packages/ui/src/forms/fieldSchemasToFormState/addFieldStatePromise.ts index 4e7666d2f1e..397beda6cb2 100644 --- a/packages/ui/src/forms/fieldSchemasToFormState/addFieldStatePromise.ts +++ b/packages/ui/src/forms/fieldSchemasToFormState/addFieldStatePromise.ts @@ -136,7 +136,7 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom const disabledFromAdmin = field?.admin && 'disabled' in field.admin && field.admin.disabled if (fieldAffectsData(field) && !(isHiddenField || disabledFromAdmin)) { - const fieldPermissions = permissions[field.name] + const fieldPermissions = permissions === true ? permissions : permissions?.[field.name] let hasPermission: boolean = fieldPermissions === true || fieldPermissions?.read @@ -382,7 +382,10 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom parentPassesCondition: passesCondition, parentPath, parentSchemaPath: rowSchemaPath, - permissions: permissions?.[field.name]?.blocks?.[block.slug]?.fields || {}, + permissions: + fieldPermissions === true + ? fieldPermissions + : permissions?.[field.name]?.blocks?.[block.slug]?.fields || {}, preferences, previousFormState, renderAllFields: requiresRender, @@ -467,7 +470,7 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom parentPassesCondition: passesCondition, parentPath: path, parentSchemaPath: schemaPath, - permissions: permissions?.[field.name]?.fields || {}, + permissions: fieldPermissions ?? permissions?.[field.name]?.fields ?? {}, preferences, previousFormState, renderAllFields, @@ -658,7 +661,11 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom parentPassesCondition: passesCondition, parentPath: tabHasName(tab) ? tabPath : parentPath, parentSchemaPath: tabHasName(tab) ? tabSchemaPath : parentSchemaPath, - permissions: tabHasName(tab) ? permissions?.[tab.name]?.fields || {} : permissions, + permissions: tabHasName(tab) + ? typeof permissions === 'boolean' + ? permissions + : permissions?.[tab.name] || {} + : permissions, preferences, previousFormState, renderAllFields, diff --git a/test/access-control/collections/RichText/index.ts b/test/access-control/collections/RichText/index.ts new file mode 100644 index 00000000000..1f670c2a458 --- /dev/null +++ b/test/access-control/collections/RichText/index.ts @@ -0,0 +1,22 @@ +import type { CollectionConfig } from 'payload' + +export const RichText: CollectionConfig = { + slug: 'rich-text', + fields: [ + { + name: 'blocks', + type: 'blocks', + blocks: [ + { + slug: 'richText', + fields: [ + { + name: 'richText', + type: 'richText', + }, + ], + }, + ], + }, + ], +} diff --git a/test/access-control/config.ts b/test/access-control/config.ts index b9e79116ce4..bbd84f42ca7 100644 --- a/test/access-control/config.ts +++ b/test/access-control/config.ts @@ -9,6 +9,7 @@ import type { Config, User } from './payload-types.js' import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js' import { devUser } from '../credentials.js' import { Disabled } from './collections/Disabled/index.js' +import { RichText } from './collections/RichText/index.js' import { createNotUpdateCollectionSlug, docLevelAccessSlug, @@ -506,6 +507,7 @@ export default buildConfigWithDefaults({ ], }, Disabled, + RichText, ], globals: [ { diff --git a/test/access-control/e2e.spec.ts b/test/access-control/e2e.spec.ts index af4ac5aef40..a9065372683 100644 --- a/test/access-control/e2e.spec.ts +++ b/test/access-control/e2e.spec.ts @@ -65,6 +65,7 @@ describe('access control', () => { let restrictedUrl: AdminUrlUtil let unrestrictedURL: AdminUrlUtil let readOnlyCollectionUrl: AdminUrlUtil + let richTextUrl: AdminUrlUtil let readOnlyGlobalUrl: AdminUrlUtil let restrictedVersionsUrl: AdminUrlUtil let userRestrictedCollectionURL: AdminUrlUtil @@ -80,6 +81,7 @@ describe('access control', () => { url = new AdminUrlUtil(serverURL, slug) restrictedUrl = new AdminUrlUtil(serverURL, fullyRestrictedSlug) + richTextUrl = new AdminUrlUtil(serverURL, 'rich-text') unrestrictedURL = new AdminUrlUtil(serverURL, unrestrictedSlug) readOnlyCollectionUrl = new AdminUrlUtil(serverURL, readOnlySlug) readOnlyGlobalUrl = new AdminUrlUtil(serverURL, readOnlySlug) @@ -147,6 +149,28 @@ describe('access control', () => { }) }) + describe('rich text', () => { + test('rich text within block should render as editable', async () => { + await page.goto(richTextUrl.create) + + await page.locator('.blocks-field__drawer-toggler').click() + await page.locator('.thumbnail-card').click() + const richTextField = page.locator('.rich-text-lexical') + const contentEditable = richTextField.locator('.ContentEditable__root').first() + await expect(contentEditable).toBeVisible() + await contentEditable.click() + + const typedText = 'Hello, this field is editable!' + await page.keyboard.type(typedText) + + await expect( + page.locator('[data-lexical-text="true"]', { + hasText: exactText(typedText), + }), + ).toHaveCount(1) + }) + }) + describe('collection — fully restricted', () => { let existingDoc: ReadOnlyCollection diff --git a/test/access-control/payload-types.ts b/test/access-control/payload-types.ts index 3096cb735f2..ff7ff52728c 100644 --- a/test/access-control/payload-types.ts +++ b/test/access-control/payload-types.ts @@ -29,6 +29,7 @@ export interface Config { 'hidden-access': HiddenAccess; 'hidden-access-count': HiddenAccessCount; disabled: Disabled; + 'rich-text': RichText; 'payload-locked-documents': PayloadLockedDocument; 'payload-preferences': PayloadPreference; 'payload-migrations': PayloadMigration; @@ -52,6 +53,7 @@ export interface Config { 'hidden-access': HiddenAccessSelect | HiddenAccessSelect; 'hidden-access-count': HiddenAccessCountSelect | HiddenAccessCountSelect; disabled: DisabledSelect | DisabledSelect; + 'rich-text': RichTextSelect | RichTextSelect; 'payload-locked-documents': PayloadLockedDocumentsSelect | PayloadLockedDocumentsSelect; 'payload-preferences': PayloadPreferencesSelect | PayloadPreferencesSelect; 'payload-migrations': PayloadMigrationsSelect | PayloadMigrationsSelect; @@ -350,6 +352,37 @@ export interface Disabled { updatedAt: string; createdAt: string; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "rich-text". + */ +export interface RichText { + id: string; + blocks?: + | { + richText?: { + root: { + type: string; + children: { + type: string; + version: number; + [k: string]: unknown; + }[]; + direction: ('ltr' | 'rtl') | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + [k: string]: unknown; + } | null; + id?: string | null; + blockName?: string | null; + blockType: 'richText'; + }[] + | null; + updatedAt: string; + createdAt: string; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-locked-documents". @@ -424,6 +457,10 @@ export interface PayloadLockedDocument { | ({ relationTo: 'disabled'; value: string | Disabled; + } | null) + | ({ + relationTo: 'rich-text'; + value: string | RichText; } | null); globalSlug?: string | null; user: @@ -694,6 +731,25 @@ export interface DisabledSelect { updatedAt?: T; createdAt?: T; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "rich-text_select". + */ +export interface RichTextSelect { + blocks?: + | T + | { + richText?: + | T + | { + richText?: T; + id?: T; + blockName?: T; + }; + }; + updatedAt?: T; + createdAt?: T; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-locked-documents_select".