fix(richtext-lexical): inline blocks and tables not functioning correctly if they are used in more than one editor on the same page (#7665)

Fixes https://github.com/payloadcms/payload/issues/7579

The problem was that multiple richtext editors shared the same drawer
slugs for the table and inline block drawers.
This commit is contained in:
Alessio Gravili
2024-08-13 21:46:23 -04:00
committed by GitHub
parent 352ed0ebef
commit fca4ee995e
4 changed files with 54 additions and 23 deletions

View File

@@ -5,7 +5,13 @@ import type { BlockFieldClient } from 'payload'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js'
import { $insertNodeToNearestRoot, $wrapNodeInElement, mergeRegister } from '@lexical/utils'
import { getTranslation } from '@payloadcms/translations'
import { useFieldProps, useModal, useTranslation } from '@payloadcms/ui'
import {
formatDrawerSlug,
useEditDepth,
useFieldProps,
useModal,
useTranslation,
} from '@payloadcms/ui'
import {
$createParagraphNode,
$getNodeByKey,
@@ -37,8 +43,6 @@ import {
export type InsertBlockPayload = Exclude<BlockFields, 'id'>
const drawerSlug = 'lexical-inlineBlocks-create'
export const BlocksPlugin: PluginComponent<BlocksFeatureClientProps> = () => {
const [editor] = useLexicalComposerContext()
const { closeModal, toggleModal } = useModal()
@@ -47,22 +51,18 @@ export const BlocksPlugin: PluginComponent<BlocksFeatureClientProps> = () => {
const [targetNodeKey, setTargetNodeKey] = useState<null | string>(null)
const { i18n, t } = useTranslation<string, any>()
const { schemaPath } = useFieldProps()
const { uuid } = useEditorConfigContext()
const editDepth = useEditDepth()
const drawerSlug = formatDrawerSlug({
slug: `lexical-inlineBlocks-create-` + uuid,
depth: editDepth,
})
const {
field: { richTextComponentMap },
} = useEditorConfigContext()
const schemaFieldsPath = `${schemaPath}.lexical_internal_feature.blocks.lexical_inline_blocks.lexical_inline_blocks.${blockFields?.blockType}`
const componentMapRenderedBlockPath = `lexical_internal_feature.blocks.fields.lexical_inline_blocks`
const blocksField: BlockFieldClient = richTextComponentMap.has(componentMapRenderedBlockPath)
? richTextComponentMap.get(componentMapRenderedBlockPath)[0]
: null
const clientBlock = blocksField
? blocksField.blocks.find((block) => block.slug === blockFields?.blockType)
: null
useEffect(() => {
if (!editor.hasNodes([BlockNode])) {
throw new Error('BlocksPlugin: BlocksNode not registered on editor')
@@ -158,7 +158,22 @@ export const BlocksPlugin: PluginComponent<BlocksFeatureClientProps> = () => {
COMMAND_PRIORITY_EDITOR,
),
)
}, [editor, targetNodeKey, toggleModal])
}, [editor, targetNodeKey, toggleModal, drawerSlug])
if (!blockFields) {
return null
}
const schemaFieldsPath = `${schemaPath}.lexical_internal_feature.blocks.lexical_inline_blocks.lexical_inline_blocks.${blockFields?.blockType}`
const componentMapRenderedBlockPath = `lexical_internal_feature.blocks.fields.lexical_inline_blocks`
const blocksField: BlockFieldClient = richTextComponentMap.has(componentMapRenderedBlockPath)
? richTextComponentMap.get(componentMapRenderedBlockPath)[0]
: null
const clientBlock = blocksField
? blocksField.blocks.find((block) => block.slug === blockFields?.blockType)
: null
if (!blocksField) {
return null

View File

@@ -14,13 +14,14 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext
import { TablePlugin as LexicalReactTablePlugin } from '@lexical/react/LexicalTablePlugin'
import { INSERT_TABLE_COMMAND, TableNode } from '@lexical/table'
import { mergeRegister } from '@lexical/utils'
import { useModal } from '@payloadcms/ui'
import { formatDrawerSlug, useEditDepth, useModal } from '@payloadcms/ui'
import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_EDITOR, createCommand } from 'lexical'
import { createContext, useContext, useEffect, useMemo, useState } from 'react'
import * as React from 'react'
import type { PluginComponent } from '../../../../typesClient.js'
import { useEditorConfigContext } from '../../../../../lexical/config/client/EditorConfigProvider.js'
import { FieldsDrawer } from '../../../../../utilities/fieldsDrawer/Drawer.js'
import './index.scss'
@@ -52,7 +53,6 @@ export const CellContext = createContext<CellContextShape>({
// Empty
},
})
const drawerSlug = 'lexical-table-create'
export function TableContext({ children }: { children: JSX.Element }) {
const [contextValue, setContextValue] = useState<{
@@ -84,6 +84,13 @@ export const TablePlugin: PluginComponent = () => {
const [editor] = useLexicalComposerContext()
const cellContext = useContext(CellContext)
const { closeModal, toggleModal } = useModal()
const editDepth = useEditDepth()
const { uuid } = useEditorConfigContext()
const drawerSlug = formatDrawerSlug({
slug: 'lexical-table-create-' + uuid,
depth: editDepth,
})
useEffect(() => {
if (!editor.hasNodes([TableNode])) {

View File

@@ -9,9 +9,10 @@ import {
Button,
DrawerToggler,
File,
formatDrawerSlug,
useConfig,
useDocumentDrawer,
useDrawerSlug,
useEditDepth,
useModal,
usePayloadAPI,
useTranslation,
@@ -25,7 +26,7 @@ import {
KEY_BACKSPACE_COMMAND,
KEY_DELETE_COMMAND,
} from 'lexical'
import React, { useCallback, useEffect, useReducer, useRef, useState } from 'react'
import React, { useCallback, useEffect, useId, useReducer, useRef, useState } from 'react'
import type { ClientComponentProps } from '../../../typesClient.js'
import type { UploadData } from '../../server/nodes/UploadNode.js'
@@ -65,7 +66,8 @@ const Component: React.FC<ElementProps> = (props) => {
} = useConfig()
const uploadRef = useRef<HTMLDivElement | null>(null)
const { closeModal } = useModal()
const { uuid } = useEditorConfigContext()
const editDepth = useEditDepth()
const [editor] = useLexicalComposerContext()
const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection(nodeKey)
@@ -77,7 +79,12 @@ const Component: React.FC<ElementProps> = (props) => {
collections.find((coll) => coll.slug === relationTo),
)
const drawerSlug = useDrawerSlug('upload-drawer')
const componentID = useId()
const drawerSlug = formatDrawerSlug({
slug: `lexical-upload-drawer-` + uuid + componentID, // There can be multiple upload components, each with their own drawer, in one single editor => separate them by componentID
depth: editDepth,
})
const [DocumentDrawer, DocumentDrawerToggler, { closeDrawer }] = useDocumentDrawer({
id: value,

View File

@@ -482,7 +482,9 @@ describe('lexicalMain', () => {
// Click on button with class lexical-upload__upload-drawer-toggler
await newUploadNode.locator('.lexical-upload__upload-drawer-toggler').first().click()
const uploadExtraFieldsDrawer = page.locator('dialog[id^=drawer_1_upload-drawer-]').first()
const uploadExtraFieldsDrawer = page
.locator('dialog[id^=drawer_1_lexical-upload-drawer-]')
.first()
await expect(uploadExtraFieldsDrawer).toBeVisible()
await wait(500)
@@ -508,7 +510,7 @@ describe('lexicalMain', () => {
await expect(reloadedUploadNode).toBeVisible()
await reloadedUploadNode.locator('.lexical-upload__upload-drawer-toggler').first().click()
const reloadedUploadExtraFieldsDrawer = page
.locator('dialog[id^=drawer_1_upload-drawer-]')
.locator('dialog[id^=drawer_1_lexical-upload-drawer-]')
.first()
await expect(reloadedUploadExtraFieldsDrawer).toBeVisible()
await wait(500)