fix(richtext-lexical): re-use payload population logic to fix population-related issues (#4291)
* chore(richtext-lexical): Add int test which reproduces the issue * chore: Remove unnecessary await in core afterRead promise * fix(richtext-lexical): re-use recurseNestedFields from payload instead of using own recurseNestedFields * chore(richtext-lexical): pass in missing properties which are available in the core afterRead hook * chore: remove unnecessary block
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import type { JSONSchema4 } from 'json-schema'
|
import type { JSONSchema4 } from 'json-schema'
|
||||||
|
|
||||||
import type { PayloadRequest } from '../../../../../express/types'
|
import type { PayloadRequest } from '../../../../../express/types'
|
||||||
|
import type { RequestContext } from '../../../../../express/types'
|
||||||
import type { RichTextField, Validate } from '../../../../../fields/config/types'
|
import type { RichTextField, Validate } from '../../../../../fields/config/types'
|
||||||
import type { CellComponentProps } from '../../../views/collections/List/Cell/types'
|
import type { CellComponentProps } from '../../../views/collections/List/Cell/types'
|
||||||
|
|
||||||
@@ -39,10 +40,14 @@ export type RichTextAdapter<
|
|||||||
isRequired: boolean
|
isRequired: boolean
|
||||||
}) => JSONSchema4
|
}) => JSONSchema4
|
||||||
populationPromise?: (data: {
|
populationPromise?: (data: {
|
||||||
|
context: RequestContext
|
||||||
currentDepth?: number
|
currentDepth?: number
|
||||||
depth: number
|
depth: number
|
||||||
field: RichTextField<Value, AdapterProps, ExtraFieldProperties>
|
field: RichTextField<Value, AdapterProps, ExtraFieldProperties>
|
||||||
|
findMany: boolean
|
||||||
|
flattenLocales: boolean
|
||||||
overrideAccess?: boolean
|
overrideAccess?: boolean
|
||||||
|
populationPromises: Promise<void>[]
|
||||||
req: PayloadRequest
|
req: PayloadRequest
|
||||||
showHiddenFields: boolean
|
showHiddenFields: boolean
|
||||||
siblingDoc: Record<string, unknown>
|
siblingDoc: Record<string, unknown>
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
export { withMergedProps } from '../admin/components/utilities/WithMergedProps'
|
export { withMergedProps } from '../admin/components/utilities/WithMergedProps'
|
||||||
export { extractTranslations } from '../translations/extractTranslations'
|
export { promise as afterReadPromise } from '../fields/hooks/afterRead/promise'
|
||||||
|
export { traverseFields as afterReadTraverseFields } from '../fields/hooks/afterRead/traverseFields'
|
||||||
|
|
||||||
|
export { extractTranslations } from '../translations/extractTranslations'
|
||||||
export { i18nInit } from '../translations/init'
|
export { i18nInit } from '../translations/init'
|
||||||
export { combineMerge } from '../utilities/combineMerge'
|
export { combineMerge } from '../utilities/combineMerge'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
configToJSONSchema,
|
configToJSONSchema,
|
||||||
entityToJSONSchema,
|
entityToJSONSchema,
|
||||||
withNullableJSONSchemaType,
|
withNullableJSONSchemaType,
|
||||||
} from '../utilities/configToJSONSchema'
|
} from '../utilities/configToJSONSchema'
|
||||||
|
|
||||||
export { createArrayFromCommaDelineated } from '../utilities/createArrayFromCommaDelineated'
|
export { createArrayFromCommaDelineated } from '../utilities/createArrayFromCommaDelineated'
|
||||||
export { deepCopyObject } from '../utilities/deepCopyObject'
|
|
||||||
|
|
||||||
|
export { deepCopyObject } from '../utilities/deepCopyObject'
|
||||||
export { deepMerge } from '../utilities/deepMerge'
|
export { deepMerge } from '../utilities/deepMerge'
|
||||||
export { fieldSchemaToJSON } from '../utilities/fieldSchemaToJSON'
|
export { fieldSchemaToJSON } from '../utilities/fieldSchemaToJSON'
|
||||||
export { default as flattenTopLevelFields } from '../utilities/flattenTopLevelFields'
|
export { default as flattenTopLevelFields } from '../utilities/flattenTopLevelFields'
|
||||||
export { formatLabels, formatNames, toWords } from '../utilities/formatLabels'
|
export { formatLabels, formatNames, toWords } from '../utilities/formatLabels'
|
||||||
export { getIDType } from '../utilities/getIDType'
|
export { getIDType } from '../utilities/getIDType'
|
||||||
export { getTranslation } from '../utilities/getTranslation'
|
export { getTranslation } from '../utilities/getTranslation'
|
||||||
|
|
||||||
export { isValidID } from '../utilities/isValidID'
|
export { isValidID } from '../utilities/isValidID'
|
||||||
|
|||||||
@@ -25,12 +25,14 @@ type Args = {
|
|||||||
req: PayloadRequest
|
req: PayloadRequest
|
||||||
showHiddenFields: boolean
|
showHiddenFields: boolean
|
||||||
siblingDoc: Record<string, unknown>
|
siblingDoc: Record<string, unknown>
|
||||||
|
triggerAccessControl?: boolean
|
||||||
|
triggerHooks?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is responsible for the following actions, in order:
|
// This function is responsible for the following actions, in order:
|
||||||
// - Remove hidden fields from response
|
// - Remove hidden fields from response
|
||||||
// - Flatten locales into requested locale
|
// - Flatten locales into requested locale
|
||||||
// - Sanitize outgoing data (point field, etc)
|
// - Sanitize outgoing data (point field, etc.)
|
||||||
// - Execute field hooks
|
// - Execute field hooks
|
||||||
// - Execute read access control
|
// - Execute read access control
|
||||||
// - Populate relationships
|
// - Populate relationships
|
||||||
@@ -51,6 +53,8 @@ export const promise = async ({
|
|||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc,
|
siblingDoc,
|
||||||
|
triggerAccessControl = true,
|
||||||
|
triggerHooks = true,
|
||||||
}: Args): Promise<void> => {
|
}: Args): Promise<void> => {
|
||||||
if (
|
if (
|
||||||
fieldAffectsData(field) &&
|
fieldAffectsData(field) &&
|
||||||
@@ -138,10 +142,14 @@ export const promise = async ({
|
|||||||
// This is run here AND in the GraphQL Resolver
|
// This is run here AND in the GraphQL Resolver
|
||||||
if (editor?.populationPromise) {
|
if (editor?.populationPromise) {
|
||||||
const populationPromise = editor.populationPromise({
|
const populationPromise = editor.populationPromise({
|
||||||
|
context,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
depth,
|
depth,
|
||||||
field,
|
field,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
overrideAccess,
|
overrideAccess,
|
||||||
|
populationPromises,
|
||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc,
|
siblingDoc,
|
||||||
@@ -186,7 +194,7 @@ export const promise = async ({
|
|||||||
|
|
||||||
if (fieldAffectsData(field)) {
|
if (fieldAffectsData(field)) {
|
||||||
// Execute hooks
|
// Execute hooks
|
||||||
if (field.hooks?.afterRead) {
|
if (triggerHooks && field.hooks?.afterRead) {
|
||||||
await field.hooks.afterRead.reduce(async (priorHook, currentHook) => {
|
await field.hooks.afterRead.reduce(async (priorHook, currentHook) => {
|
||||||
await priorHook
|
await priorHook
|
||||||
|
|
||||||
@@ -241,7 +249,7 @@ export const promise = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Execute access control
|
// Execute access control
|
||||||
if (field.access && field.access.read) {
|
if (triggerAccessControl && field.access && field.access.read) {
|
||||||
const result = overrideAccess
|
const result = overrideAccess
|
||||||
? true
|
? true
|
||||||
: await field.access.read({
|
: await field.access.read({
|
||||||
@@ -293,6 +301,8 @@ export const promise = async ({
|
|||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc: groupDoc,
|
siblingDoc: groupDoc,
|
||||||
|
triggerAccessControl,
|
||||||
|
triggerHooks,
|
||||||
})
|
})
|
||||||
|
|
||||||
break
|
break
|
||||||
@@ -319,6 +329,8 @@ export const promise = async ({
|
|||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc: row || {},
|
siblingDoc: row || {},
|
||||||
|
triggerAccessControl,
|
||||||
|
triggerHooks,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else if (!shouldHoistLocalizedValue && typeof rows === 'object' && rows !== null) {
|
} else if (!shouldHoistLocalizedValue && typeof rows === 'object' && rows !== null) {
|
||||||
@@ -341,6 +353,8 @@ export const promise = async ({
|
|||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc: row || {},
|
siblingDoc: row || {},
|
||||||
|
triggerAccessControl,
|
||||||
|
triggerHooks,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -375,6 +389,8 @@ export const promise = async ({
|
|||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc: row || {},
|
siblingDoc: row || {},
|
||||||
|
triggerAccessControl,
|
||||||
|
triggerHooks,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -401,6 +417,8 @@ export const promise = async ({
|
|||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc: row || {},
|
siblingDoc: row || {},
|
||||||
|
triggerAccessControl,
|
||||||
|
triggerHooks,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -431,6 +449,8 @@ export const promise = async ({
|
|||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc,
|
siblingDoc,
|
||||||
|
triggerAccessControl,
|
||||||
|
triggerHooks,
|
||||||
})
|
})
|
||||||
|
|
||||||
break
|
break
|
||||||
@@ -443,7 +463,7 @@ export const promise = async ({
|
|||||||
if (typeof siblingDoc[field.name] !== 'object') tabDoc = {}
|
if (typeof siblingDoc[field.name] !== 'object') tabDoc = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
await traverseFields({
|
traverseFields({
|
||||||
collection,
|
collection,
|
||||||
context,
|
context,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
@@ -459,6 +479,8 @@ export const promise = async ({
|
|||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc: tabDoc,
|
siblingDoc: tabDoc,
|
||||||
|
triggerAccessControl,
|
||||||
|
triggerHooks,
|
||||||
})
|
})
|
||||||
|
|
||||||
break
|
break
|
||||||
@@ -481,6 +503,8 @@ export const promise = async ({
|
|||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc,
|
siblingDoc,
|
||||||
|
triggerAccessControl,
|
||||||
|
triggerHooks,
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ type Args = {
|
|||||||
req: PayloadRequest
|
req: PayloadRequest
|
||||||
showHiddenFields: boolean
|
showHiddenFields: boolean
|
||||||
siblingDoc: Record<string, unknown>
|
siblingDoc: Record<string, unknown>
|
||||||
|
triggerAccessControl?: boolean
|
||||||
|
triggerHooks?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const traverseFields = ({
|
export const traverseFields = ({
|
||||||
@@ -39,6 +41,8 @@ export const traverseFields = ({
|
|||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc,
|
siblingDoc,
|
||||||
|
triggerAccessControl = true,
|
||||||
|
triggerHooks = true,
|
||||||
}: Args): void => {
|
}: Args): void => {
|
||||||
fields.forEach((field) => {
|
fields.forEach((field) => {
|
||||||
fieldPromises.push(
|
fieldPromises.push(
|
||||||
@@ -58,6 +62,8 @@ export const traverseFields = ({
|
|||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc,
|
siblingDoc,
|
||||||
|
triggerAccessControl,
|
||||||
|
triggerHooks,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -436,8 +436,13 @@ function buildObjectType({
|
|||||||
// Effectively, this means that the populationPromise for GraphQL is only run here, and not in the find.ts resolver / normal population promise.
|
// Effectively, this means that the populationPromise for GraphQL is only run here, and not in the find.ts resolver / normal population promise.
|
||||||
if (editor?.populationPromise) {
|
if (editor?.populationPromise) {
|
||||||
await editor?.populationPromise({
|
await editor?.populationPromise({
|
||||||
|
context,
|
||||||
depth,
|
depth,
|
||||||
field,
|
field,
|
||||||
|
findMany: false,
|
||||||
|
flattenLocales: false,
|
||||||
|
overrideAccess: false,
|
||||||
|
populationPromises: [],
|
||||||
req: context.req,
|
req: context.req,
|
||||||
showHiddenFields: false,
|
showHiddenFields: false,
|
||||||
siblingDoc: parent,
|
siblingDoc: parent,
|
||||||
|
|||||||
@@ -12,13 +12,18 @@ export const blockPopulationPromiseHOC = (
|
|||||||
props: BlocksFeatureProps,
|
props: BlocksFeatureProps,
|
||||||
): PopulationPromise<SerializedBlockNode> => {
|
): PopulationPromise<SerializedBlockNode> => {
|
||||||
const blockPopulationPromise: PopulationPromise<SerializedBlockNode> = ({
|
const blockPopulationPromise: PopulationPromise<SerializedBlockNode> = ({
|
||||||
|
context,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
depth,
|
depth,
|
||||||
|
editorPopulationPromises,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
node,
|
node,
|
||||||
overrideAccess,
|
overrideAccess,
|
||||||
populationPromises,
|
populationPromises,
|
||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
|
siblingDoc,
|
||||||
}) => {
|
}) => {
|
||||||
const blocks: Block[] = props.blocks
|
const blocks: Block[] = props.blocks
|
||||||
const blockFieldData = node.fields
|
const blockFieldData = node.fields
|
||||||
@@ -43,10 +48,14 @@ export const blockPopulationPromiseHOC = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
recurseNestedFields({
|
recurseNestedFields({
|
||||||
|
context,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
data: blockFieldData,
|
data: blockFieldData,
|
||||||
depth,
|
depth,
|
||||||
|
editorPopulationPromises,
|
||||||
fields: block.fields,
|
fields: block.fields,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
overrideAccess,
|
overrideAccess,
|
||||||
populationPromises,
|
populationPromises,
|
||||||
promises,
|
promises,
|
||||||
|
|||||||
@@ -9,14 +9,19 @@ export const linkPopulationPromiseHOC = (
|
|||||||
props: LinkFeatureProps,
|
props: LinkFeatureProps,
|
||||||
): PopulationPromise<SerializedLinkNode> => {
|
): PopulationPromise<SerializedLinkNode> => {
|
||||||
const linkPopulationPromise: PopulationPromise<SerializedLinkNode> = ({
|
const linkPopulationPromise: PopulationPromise<SerializedLinkNode> = ({
|
||||||
|
context,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
depth,
|
depth,
|
||||||
|
editorPopulationPromises,
|
||||||
field,
|
field,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
node,
|
node,
|
||||||
overrideAccess,
|
overrideAccess,
|
||||||
populationPromises,
|
populationPromises,
|
||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
|
siblingDoc,
|
||||||
}) => {
|
}) => {
|
||||||
const promises: Promise<void>[] = []
|
const promises: Promise<void>[] = []
|
||||||
|
|
||||||
@@ -42,10 +47,14 @@ export const linkPopulationPromiseHOC = (
|
|||||||
}
|
}
|
||||||
if (Array.isArray(props.fields)) {
|
if (Array.isArray(props.fields)) {
|
||||||
recurseNestedFields({
|
recurseNestedFields({
|
||||||
|
context,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
data: node.fields || {},
|
data: node.fields || {},
|
||||||
depth,
|
depth,
|
||||||
|
editorPopulationPromises,
|
||||||
fields: props.fields,
|
fields: props.fields,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
overrideAccess,
|
overrideAccess,
|
||||||
populationPromises,
|
populationPromises,
|
||||||
promises,
|
promises,
|
||||||
|
|||||||
@@ -9,14 +9,19 @@ export const uploadPopulationPromiseHOC = (
|
|||||||
props?: UploadFeatureProps,
|
props?: UploadFeatureProps,
|
||||||
): PopulationPromise<SerializedUploadNode> => {
|
): PopulationPromise<SerializedUploadNode> => {
|
||||||
const uploadPopulationPromise: PopulationPromise<SerializedUploadNode> = ({
|
const uploadPopulationPromise: PopulationPromise<SerializedUploadNode> = ({
|
||||||
|
context,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
depth,
|
depth,
|
||||||
|
editorPopulationPromises,
|
||||||
field,
|
field,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
node,
|
node,
|
||||||
overrideAccess,
|
overrideAccess,
|
||||||
populationPromises,
|
populationPromises,
|
||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
|
siblingDoc,
|
||||||
}) => {
|
}) => {
|
||||||
const promises: Promise<void>[] = []
|
const promises: Promise<void>[] = []
|
||||||
|
|
||||||
@@ -41,10 +46,14 @@ export const uploadPopulationPromiseHOC = (
|
|||||||
}
|
}
|
||||||
if (Array.isArray(props?.collections?.[node?.relationTo]?.fields)) {
|
if (Array.isArray(props?.collections?.[node?.relationTo]?.fields)) {
|
||||||
recurseNestedFields({
|
recurseNestedFields({
|
||||||
|
context,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
data: node.fields || {},
|
data: node.fields || {},
|
||||||
depth,
|
depth,
|
||||||
|
editorPopulationPromises,
|
||||||
fields: props?.collections?.[node?.relationTo]?.fields,
|
fields: props?.collections?.[node?.relationTo]?.fields,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
overrideAccess,
|
overrideAccess,
|
||||||
populationPromises,
|
populationPromises,
|
||||||
promises,
|
promises,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { Transformer } from '@lexical/markdown'
|
|||||||
import type { Klass, LexicalEditor, LexicalNode, SerializedEditorState } from 'lexical'
|
import type { Klass, LexicalEditor, LexicalNode, SerializedEditorState } from 'lexical'
|
||||||
import type { SerializedLexicalNode } from 'lexical'
|
import type { SerializedLexicalNode } from 'lexical'
|
||||||
import type { LexicalNodeReplacement } from 'lexical'
|
import type { LexicalNodeReplacement } from 'lexical'
|
||||||
|
import type { RequestContext } from 'payload'
|
||||||
import type { SanitizedConfig } from 'payload/config'
|
import type { SanitizedConfig } from 'payload/config'
|
||||||
import type { PayloadRequest, RichTextField, ValidateOptions } from 'payload/types'
|
import type { PayloadRequest, RichTextField, ValidateOptions } from 'payload/types'
|
||||||
import type React from 'react'
|
import type React from 'react'
|
||||||
@@ -13,9 +14,13 @@ import type { SlashMenuGroup } from '../lexical/plugins/SlashMenu/LexicalTypeahe
|
|||||||
import type { HTMLConverter } from './converters/html/converter/types'
|
import type { HTMLConverter } from './converters/html/converter/types'
|
||||||
|
|
||||||
export type PopulationPromise<T extends SerializedLexicalNode = SerializedLexicalNode> = ({
|
export type PopulationPromise<T extends SerializedLexicalNode = SerializedLexicalNode> = ({
|
||||||
|
context,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
depth,
|
depth,
|
||||||
|
editorPopulationPromises,
|
||||||
field,
|
field,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
node,
|
node,
|
||||||
overrideAccess,
|
overrideAccess,
|
||||||
populationPromises,
|
populationPromises,
|
||||||
@@ -23,12 +28,19 @@ export type PopulationPromise<T extends SerializedLexicalNode = SerializedLexica
|
|||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc,
|
siblingDoc,
|
||||||
}: {
|
}: {
|
||||||
|
context: RequestContext
|
||||||
currentDepth: number
|
currentDepth: number
|
||||||
depth: number
|
depth: number
|
||||||
|
/**
|
||||||
|
* This maps all population promises to the node type
|
||||||
|
*/
|
||||||
|
editorPopulationPromises: Map<string, Array<PopulationPromise>>
|
||||||
field: RichTextField<SerializedEditorState, AdapterProps>
|
field: RichTextField<SerializedEditorState, AdapterProps>
|
||||||
|
findMany: boolean
|
||||||
|
flattenLocales: boolean
|
||||||
node: T
|
node: T
|
||||||
overrideAccess: boolean
|
overrideAccess: boolean
|
||||||
populationPromises: Map<string, Array<PopulationPromise>>
|
populationPromises: Promise<void>[]
|
||||||
req: PayloadRequest
|
req: PayloadRequest
|
||||||
showHiddenFields: boolean
|
showHiddenFields: boolean
|
||||||
siblingDoc: Record<string, unknown>
|
siblingDoc: Record<string, unknown>
|
||||||
|
|||||||
@@ -148,10 +148,14 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
populationPromise({
|
populationPromise({
|
||||||
|
context,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
depth,
|
depth,
|
||||||
field,
|
field,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
overrideAccess,
|
overrideAccess,
|
||||||
|
populationPromises,
|
||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc,
|
siblingDoc,
|
||||||
@@ -159,11 +163,15 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte
|
|||||||
// check if there are any features with nodes which have populationPromises for this field
|
// check if there are any features with nodes which have populationPromises for this field
|
||||||
if (finalSanitizedEditorConfig?.features?.populationPromises?.size) {
|
if (finalSanitizedEditorConfig?.features?.populationPromises?.size) {
|
||||||
return richTextRelationshipPromise({
|
return richTextRelationshipPromise({
|
||||||
|
context,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
depth,
|
depth,
|
||||||
|
editorPopulationPromises: finalSanitizedEditorConfig.features.populationPromises,
|
||||||
field,
|
field,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
overrideAccess,
|
overrideAccess,
|
||||||
populationPromises: finalSanitizedEditorConfig.features.populationPromises,
|
populationPromises,
|
||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc,
|
siblingDoc,
|
||||||
|
|||||||
@@ -1,18 +1,24 @@
|
|||||||
import type { Field, PayloadRequest, RichTextAdapter } from 'payload/types'
|
import type { RequestContext } from 'payload'
|
||||||
|
import type { Field, PayloadRequest } from 'payload/types'
|
||||||
|
|
||||||
import { fieldAffectsData, fieldHasSubFields, fieldIsArrayType } from 'payload/types'
|
import { afterReadTraverseFields } from 'payload/utilities'
|
||||||
|
|
||||||
import type { PopulationPromise } from '../field/features/types'
|
import type { PopulationPromise } from '../field/features/types'
|
||||||
|
|
||||||
import { populate } from './populate'
|
|
||||||
|
|
||||||
type NestedRichTextFieldsArgs = {
|
type NestedRichTextFieldsArgs = {
|
||||||
|
context: RequestContext
|
||||||
currentDepth?: number
|
currentDepth?: number
|
||||||
data: unknown
|
data: unknown
|
||||||
depth: number
|
depth: number
|
||||||
|
/**
|
||||||
|
* This maps all the population promises to the node types
|
||||||
|
*/
|
||||||
|
editorPopulationPromises: Map<string, Array<PopulationPromise>>
|
||||||
fields: Field[]
|
fields: Field[]
|
||||||
|
findMany: boolean
|
||||||
|
flattenLocales: boolean
|
||||||
overrideAccess: boolean
|
overrideAccess: boolean
|
||||||
populationPromises: Map<string, Array<PopulationPromise>>
|
populationPromises: Promise<void>[]
|
||||||
promises: Promise<void>[]
|
promises: Promise<void>[]
|
||||||
req: PayloadRequest
|
req: PayloadRequest
|
||||||
showHiddenFields: boolean
|
showHiddenFields: boolean
|
||||||
@@ -20,10 +26,13 @@ type NestedRichTextFieldsArgs = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const recurseNestedFields = ({
|
export const recurseNestedFields = ({
|
||||||
|
context,
|
||||||
currentDepth = 0,
|
currentDepth = 0,
|
||||||
data,
|
data,
|
||||||
depth,
|
depth,
|
||||||
fields,
|
fields,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
overrideAccess = false,
|
overrideAccess = false,
|
||||||
populationPromises,
|
populationPromises,
|
||||||
promises,
|
promises,
|
||||||
@@ -31,193 +40,23 @@ export const recurseNestedFields = ({
|
|||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc,
|
siblingDoc,
|
||||||
}: NestedRichTextFieldsArgs): void => {
|
}: NestedRichTextFieldsArgs): void => {
|
||||||
fields.forEach((field) => {
|
afterReadTraverseFields({
|
||||||
if (field.type === 'relationship' || field.type === 'upload') {
|
collection: null, // Pass from core? This is only needed for hooks, so we can leave this null for now
|
||||||
if (field.type === 'relationship') {
|
context,
|
||||||
if (field.hasMany && Array.isArray(data[field.name])) {
|
currentDepth,
|
||||||
if (Array.isArray(field.relationTo)) {
|
depth,
|
||||||
// polymorphic relationship
|
doc: data as any, // Looks like it's only needed for hooks and access control, so doesn't matter what we pass here right now
|
||||||
data[field.name].forEach(({ relationTo, value }, i) => {
|
fieldPromises: promises, // Not sure if what I pass in here makes sense. But it doesn't seem like it's used at all anyways
|
||||||
const collection = req.payload.collections[relationTo]
|
fields,
|
||||||
if (collection) {
|
findMany,
|
||||||
promises.push(
|
flattenLocales,
|
||||||
populate({
|
global: null, // Pass from core? This is only needed for hooks, so we can leave this null for now
|
||||||
id: value,
|
overrideAccess,
|
||||||
collection,
|
populationPromises, // This is not the same as populationPromises passed into this recurseNestedFields. These are just promises resolved at the very end.
|
||||||
currentDepth,
|
req,
|
||||||
data: data[field.name],
|
showHiddenFields,
|
||||||
depth,
|
siblingDoc,
|
||||||
field,
|
triggerAccessControl: false, // TODO: Enable this to support access control
|
||||||
key: i,
|
triggerHooks: false, // TODO: Enable this to support hooks
|
||||||
overrideAccess,
|
|
||||||
req,
|
|
||||||
showHiddenFields,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
data[field.name].forEach((id, i) => {
|
|
||||||
const collection = req.payload.collections[field.relationTo as string]
|
|
||||||
if (collection) {
|
|
||||||
promises.push(
|
|
||||||
populate({
|
|
||||||
id,
|
|
||||||
collection,
|
|
||||||
currentDepth,
|
|
||||||
data: data[field.name],
|
|
||||||
depth,
|
|
||||||
field,
|
|
||||||
key: i,
|
|
||||||
overrideAccess,
|
|
||||||
req,
|
|
||||||
showHiddenFields,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
Array.isArray(field.relationTo) &&
|
|
||||||
data[field.name]?.value &&
|
|
||||||
data[field.name]?.relationTo
|
|
||||||
) {
|
|
||||||
const collection = req.payload.collections[data[field.name].relationTo]
|
|
||||||
promises.push(
|
|
||||||
populate({
|
|
||||||
id: data[field.name].value,
|
|
||||||
collection,
|
|
||||||
currentDepth,
|
|
||||||
data: data[field.name],
|
|
||||||
depth,
|
|
||||||
field,
|
|
||||||
key: 'value',
|
|
||||||
overrideAccess,
|
|
||||||
req,
|
|
||||||
showHiddenFields,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (typeof data[field.name] !== 'undefined' && typeof field.relationTo === 'string') {
|
|
||||||
if (!('hasMany' in field) || !field.hasMany) {
|
|
||||||
const collection = req.payload.collections[field.relationTo]
|
|
||||||
promises.push(
|
|
||||||
populate({
|
|
||||||
id: data[field.name],
|
|
||||||
collection,
|
|
||||||
currentDepth,
|
|
||||||
data,
|
|
||||||
depth,
|
|
||||||
field,
|
|
||||||
key: field.name,
|
|
||||||
overrideAccess,
|
|
||||||
req,
|
|
||||||
showHiddenFields,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (fieldHasSubFields(field) && !fieldIsArrayType(field)) {
|
|
||||||
if (fieldAffectsData(field) && typeof data[field.name] === 'object') {
|
|
||||||
recurseNestedFields({
|
|
||||||
currentDepth,
|
|
||||||
data: data[field.name],
|
|
||||||
depth,
|
|
||||||
fields: field.fields,
|
|
||||||
overrideAccess,
|
|
||||||
populationPromises,
|
|
||||||
promises,
|
|
||||||
req,
|
|
||||||
showHiddenFields,
|
|
||||||
siblingDoc,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
recurseNestedFields({
|
|
||||||
currentDepth,
|
|
||||||
data,
|
|
||||||
depth,
|
|
||||||
fields: field.fields,
|
|
||||||
overrideAccess,
|
|
||||||
populationPromises,
|
|
||||||
promises,
|
|
||||||
req,
|
|
||||||
showHiddenFields,
|
|
||||||
siblingDoc,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else if (field.type === 'tabs') {
|
|
||||||
field.tabs.forEach((tab) => {
|
|
||||||
recurseNestedFields({
|
|
||||||
currentDepth,
|
|
||||||
data,
|
|
||||||
depth,
|
|
||||||
fields: tab.fields,
|
|
||||||
overrideAccess,
|
|
||||||
populationPromises,
|
|
||||||
promises,
|
|
||||||
req,
|
|
||||||
showHiddenFields,
|
|
||||||
siblingDoc,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else if (Array.isArray(data[field.name])) {
|
|
||||||
if (field.type === 'blocks') {
|
|
||||||
data[field.name].forEach((row, i) => {
|
|
||||||
const block = field.blocks.find(({ slug }) => slug === row?.blockType)
|
|
||||||
if (block) {
|
|
||||||
recurseNestedFields({
|
|
||||||
currentDepth,
|
|
||||||
data: data[field.name][i],
|
|
||||||
depth,
|
|
||||||
fields: block.fields,
|
|
||||||
overrideAccess,
|
|
||||||
populationPromises,
|
|
||||||
promises,
|
|
||||||
req,
|
|
||||||
showHiddenFields,
|
|
||||||
siblingDoc: data[field.name][i], // This has to be scoped to the blocks's fields, otherwise there may be population issues, e.g. for a relationship field with Blocks Node, with a Blocks Field, with a RichText Field, With Relationship Node. The last richtext field would try to find itself using siblingDoc[field.nane], which only works if the siblingDoc is scoped to the blocks's fields
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (field.type === 'array') {
|
|
||||||
data[field.name].forEach((_, i) => {
|
|
||||||
recurseNestedFields({
|
|
||||||
currentDepth,
|
|
||||||
data: data[field.name][i],
|
|
||||||
depth,
|
|
||||||
fields: field.fields,
|
|
||||||
overrideAccess,
|
|
||||||
populationPromises,
|
|
||||||
promises,
|
|
||||||
req,
|
|
||||||
showHiddenFields,
|
|
||||||
siblingDoc, // TODO: if there's any population issues, this might have to be data[field.name][i] as well
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (field.type === 'richText') {
|
|
||||||
const editor: RichTextAdapter = field?.editor
|
|
||||||
|
|
||||||
if (editor?.populationPromise) {
|
|
||||||
const afterReadPromise = editor.populationPromise({
|
|
||||||
currentDepth,
|
|
||||||
depth,
|
|
||||||
field,
|
|
||||||
overrideAccess,
|
|
||||||
req,
|
|
||||||
showHiddenFields,
|
|
||||||
siblingDoc,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (afterReadPromise) {
|
|
||||||
promises.push(afterReadPromise)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,16 +7,16 @@ import type { AdapterProps } from '../types'
|
|||||||
export type Args = Parameters<
|
export type Args = Parameters<
|
||||||
RichTextAdapter<SerializedEditorState, AdapterProps>['populationPromise']
|
RichTextAdapter<SerializedEditorState, AdapterProps>['populationPromise']
|
||||||
>[0] & {
|
>[0] & {
|
||||||
populationPromises: Map<string, Array<PopulationPromise>>
|
editorPopulationPromises: Map<string, Array<PopulationPromise>>
|
||||||
}
|
}
|
||||||
|
|
||||||
type RecurseRichTextArgs = {
|
type RecurseRichTextArgs = {
|
||||||
children: SerializedLexicalNode[]
|
children: SerializedLexicalNode[]
|
||||||
currentDepth: number
|
currentDepth: number
|
||||||
depth: number
|
depth: number
|
||||||
|
editorPopulationPromises: Map<string, Array<PopulationPromise>>
|
||||||
field: RichTextField<SerializedEditorState, AdapterProps>
|
field: RichTextField<SerializedEditorState, AdapterProps>
|
||||||
overrideAccess: boolean
|
overrideAccess: boolean
|
||||||
populationPromises: Map<string, Array<PopulationPromise>>
|
|
||||||
promises: Promise<void>[]
|
promises: Promise<void>[]
|
||||||
req: PayloadRequest
|
req: PayloadRequest
|
||||||
showHiddenFields: boolean
|
showHiddenFields: boolean
|
||||||
@@ -25,29 +25,37 @@ type RecurseRichTextArgs = {
|
|||||||
|
|
||||||
export const recurseRichText = ({
|
export const recurseRichText = ({
|
||||||
children,
|
children,
|
||||||
|
context,
|
||||||
currentDepth = 0,
|
currentDepth = 0,
|
||||||
depth,
|
depth,
|
||||||
|
editorPopulationPromises,
|
||||||
field,
|
field,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
overrideAccess = false,
|
overrideAccess = false,
|
||||||
populationPromises,
|
populationPromises,
|
||||||
promises,
|
promises,
|
||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc,
|
siblingDoc,
|
||||||
}: RecurseRichTextArgs): void => {
|
}: RecurseRichTextArgs & Args): void => {
|
||||||
if (depth <= 0 || currentDepth > depth) {
|
if (depth <= 0 || currentDepth > depth) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(children)) {
|
if (Array.isArray(children)) {
|
||||||
children.forEach((node) => {
|
children.forEach((node) => {
|
||||||
if (populationPromises?.has(node.type)) {
|
if (editorPopulationPromises?.has(node.type)) {
|
||||||
for (const promise of populationPromises.get(node.type)) {
|
for (const promise of editorPopulationPromises.get(node.type)) {
|
||||||
promises.push(
|
promises.push(
|
||||||
...promise({
|
...promise({
|
||||||
|
context,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
depth,
|
depth,
|
||||||
|
editorPopulationPromises,
|
||||||
field,
|
field,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
node: node,
|
node: node,
|
||||||
overrideAccess,
|
overrideAccess,
|
||||||
populationPromises,
|
populationPromises,
|
||||||
@@ -62,9 +70,13 @@ export const recurseRichText = ({
|
|||||||
if ('children' in node && Array.isArray(node?.children) && node?.children?.length) {
|
if ('children' in node && Array.isArray(node?.children) && node?.children?.length) {
|
||||||
recurseRichText({
|
recurseRichText({
|
||||||
children: node.children as SerializedLexicalNode[],
|
children: node.children as SerializedLexicalNode[],
|
||||||
|
context,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
depth,
|
depth,
|
||||||
|
editorPopulationPromises,
|
||||||
field,
|
field,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
overrideAccess,
|
overrideAccess,
|
||||||
populationPromises,
|
populationPromises,
|
||||||
promises,
|
promises,
|
||||||
@@ -78,9 +90,13 @@ export const recurseRichText = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const richTextRelationshipPromise = async ({
|
export const richTextRelationshipPromise = async ({
|
||||||
|
context,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
depth,
|
depth,
|
||||||
|
editorPopulationPromises,
|
||||||
field,
|
field,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
overrideAccess,
|
overrideAccess,
|
||||||
populationPromises,
|
populationPromises,
|
||||||
req,
|
req,
|
||||||
@@ -91,9 +107,13 @@ export const richTextRelationshipPromise = async ({
|
|||||||
|
|
||||||
recurseRichText({
|
recurseRichText({
|
||||||
children: (siblingDoc[field?.name] as SerializedEditorState)?.root?.children ?? [],
|
children: (siblingDoc[field?.name] as SerializedEditorState)?.root?.children ?? [],
|
||||||
|
context,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
depth,
|
depth,
|
||||||
|
editorPopulationPromises,
|
||||||
field,
|
field,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
overrideAccess,
|
overrideAccess,
|
||||||
populationPromises,
|
populationPromises,
|
||||||
promises,
|
promises,
|
||||||
|
|||||||
@@ -30,10 +30,14 @@ export function slateEditor(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
populationPromise({
|
populationPromise({
|
||||||
|
context,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
depth,
|
depth,
|
||||||
field,
|
field,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
overrideAccess,
|
overrideAccess,
|
||||||
|
populationPromises,
|
||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc,
|
siblingDoc,
|
||||||
@@ -45,10 +49,14 @@ export function slateEditor(
|
|||||||
!field?.admin?.elements
|
!field?.admin?.elements
|
||||||
) {
|
) {
|
||||||
return richTextRelationshipPromise({
|
return richTextRelationshipPromise({
|
||||||
|
context,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
depth,
|
depth,
|
||||||
field,
|
field,
|
||||||
|
findMany,
|
||||||
|
flattenLocales,
|
||||||
overrideAccess,
|
overrideAccess,
|
||||||
|
populationPromises,
|
||||||
req,
|
req,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
siblingDoc,
|
siblingDoc,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { Block } from '../../../../packages/payload/src/fields/config/types'
|
import type { Block } from '../../../../packages/payload/src/fields/config/types'
|
||||||
|
|
||||||
import { lexicalEditor } from '../../../../packages/richtext-lexical/src'
|
import { lexicalEditor } from '../../../../packages/richtext-lexical/src'
|
||||||
|
import { textFieldsSlug } from '../Text/shared'
|
||||||
|
|
||||||
export const BlockColumns: any = {
|
export const BlockColumns: any = {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
@@ -123,6 +124,18 @@ export const UploadAndRichTextBlock: Block = {
|
|||||||
slug: 'uploadAndRichText',
|
slug: 'uploadAndRichText',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const RelationshipHasManyBlock: Block = {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'rel',
|
||||||
|
type: 'relationship',
|
||||||
|
hasMany: true,
|
||||||
|
relationTo: [textFieldsSlug, 'uploads'],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
slug: 'relationshipHasManyBlock',
|
||||||
|
}
|
||||||
export const RelationshipBlock: Block = {
|
export const RelationshipBlock: Block = {
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -84,6 +84,26 @@ export function generateLexicalRichText() {
|
|||||||
blockType: 'relationshipBlock',
|
blockType: 'relationshipBlock',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
format: '',
|
||||||
|
type: 'block',
|
||||||
|
version: 2,
|
||||||
|
fields: {
|
||||||
|
id: '6565c8668294bf824c24d4a4',
|
||||||
|
blockName: '',
|
||||||
|
blockType: 'relationshipHasManyBlock',
|
||||||
|
rel: [
|
||||||
|
{
|
||||||
|
value: '{{TEXT_DOC_ID}}',
|
||||||
|
relationTo: 'text-fields',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '{{UPLOAD_DOC_ID}}',
|
||||||
|
relationTo: 'uploads',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
format: '',
|
format: '',
|
||||||
type: 'block',
|
type: 'block',
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
ConditionalLayoutBlock,
|
ConditionalLayoutBlock,
|
||||||
RadioButtonsBlock,
|
RadioButtonsBlock,
|
||||||
RelationshipBlock,
|
RelationshipBlock,
|
||||||
|
RelationshipHasManyBlock,
|
||||||
RichTextBlock,
|
RichTextBlock,
|
||||||
SelectFieldBlock,
|
SelectFieldBlock,
|
||||||
SubBlockBlock,
|
SubBlockBlock,
|
||||||
@@ -49,6 +50,7 @@ export const LexicalFields: CollectionConfig = {
|
|||||||
UploadAndRichTextBlock,
|
UploadAndRichTextBlock,
|
||||||
SelectFieldBlock,
|
SelectFieldBlock,
|
||||||
RelationshipBlock,
|
RelationshipBlock,
|
||||||
|
RelationshipHasManyBlock,
|
||||||
SubBlockBlock,
|
SubBlockBlock,
|
||||||
RadioButtonsBlock,
|
RadioButtonsBlock,
|
||||||
ConditionalLayoutBlock,
|
ConditionalLayoutBlock,
|
||||||
@@ -102,6 +104,7 @@ export const LexicalFields: CollectionConfig = {
|
|||||||
UploadAndRichTextBlock,
|
UploadAndRichTextBlock,
|
||||||
SelectFieldBlock,
|
SelectFieldBlock,
|
||||||
RelationshipBlock,
|
RelationshipBlock,
|
||||||
|
RelationshipHasManyBlock,
|
||||||
SubBlockBlock,
|
SubBlockBlock,
|
||||||
RadioButtonsBlock,
|
RadioButtonsBlock,
|
||||||
ConditionalLayoutBlock,
|
ConditionalLayoutBlock,
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ describe('lexical', () => {
|
|||||||
await richTextField.scrollIntoViewIfNeeded()
|
await richTextField.scrollIntoViewIfNeeded()
|
||||||
await expect(richTextField).toBeVisible()
|
await expect(richTextField).toBeVisible()
|
||||||
|
|
||||||
const lexicalBlock = richTextField.locator('.lexical-block').nth(1) // second: "Block Node, with RichText Field, with Relationship Node"
|
const lexicalBlock = richTextField.locator('.lexical-block').nth(2) // third: "Block Node, with RichText Field, with Relationship Node"
|
||||||
await lexicalBlock.scrollIntoViewIfNeeded()
|
await lexicalBlock.scrollIntoViewIfNeeded()
|
||||||
await expect(lexicalBlock).toBeVisible()
|
await expect(lexicalBlock).toBeVisible()
|
||||||
|
|
||||||
@@ -225,7 +225,7 @@ describe('lexical', () => {
|
|||||||
).docs[0] as never
|
).docs[0] as never
|
||||||
|
|
||||||
const lexicalField: SerializedEditorState = lexicalDoc.lexicalWithBlocks
|
const lexicalField: SerializedEditorState = lexicalDoc.lexicalWithBlocks
|
||||||
const blockNode: SerializedBlockNode = lexicalField.root.children[3] as SerializedBlockNode
|
const blockNode: SerializedBlockNode = lexicalField.root.children[4] as SerializedBlockNode
|
||||||
const textNodeInBlockNodeRichText = blockNode.fields.richText.root.children[1].children[0]
|
const textNodeInBlockNodeRichText = blockNode.fields.richText.root.children[1].children[0]
|
||||||
|
|
||||||
expect(textNodeInBlockNodeRichText.text).toBe(
|
expect(textNodeInBlockNodeRichText.text).toBe(
|
||||||
@@ -239,7 +239,7 @@ describe('lexical', () => {
|
|||||||
await richTextField.scrollIntoViewIfNeeded()
|
await richTextField.scrollIntoViewIfNeeded()
|
||||||
await expect(richTextField).toBeVisible()
|
await expect(richTextField).toBeVisible()
|
||||||
|
|
||||||
const lexicalBlock = richTextField.locator('.lexical-block').nth(1) // second: "Block Node, with RichText Field, with Relationship Node"
|
const lexicalBlock = richTextField.locator('.lexical-block').nth(2) // third: "Block Node, with RichText Field, with Relationship Node"
|
||||||
await lexicalBlock.scrollIntoViewIfNeeded()
|
await lexicalBlock.scrollIntoViewIfNeeded()
|
||||||
await expect(lexicalBlock).toBeVisible()
|
await expect(lexicalBlock).toBeVisible()
|
||||||
|
|
||||||
@@ -298,7 +298,7 @@ describe('lexical', () => {
|
|||||||
).docs[0] as never
|
).docs[0] as never
|
||||||
|
|
||||||
const lexicalField: SerializedEditorState = lexicalDoc.lexicalWithBlocks
|
const lexicalField: SerializedEditorState = lexicalDoc.lexicalWithBlocks
|
||||||
const blockNode: SerializedBlockNode = lexicalField.root.children[3] as SerializedBlockNode
|
const blockNode: SerializedBlockNode = lexicalField.root.children[4] as SerializedBlockNode
|
||||||
const paragraphNodeInBlockNodeRichText = blockNode.fields.richText.root.children[1]
|
const paragraphNodeInBlockNodeRichText = blockNode.fields.richText.root.children[1]
|
||||||
|
|
||||||
expect(paragraphNodeInBlockNodeRichText.children).toHaveLength(2)
|
expect(paragraphNodeInBlockNodeRichText.children).toHaveLength(2)
|
||||||
@@ -319,7 +319,7 @@ describe('lexical', () => {
|
|||||||
await richTextField.scrollIntoViewIfNeeded()
|
await richTextField.scrollIntoViewIfNeeded()
|
||||||
await expect(richTextField).toBeVisible()
|
await expect(richTextField).toBeVisible()
|
||||||
|
|
||||||
const lexicalBlock = richTextField.locator('.lexical-block').nth(1) // secondL: "Block Node, with RichText Field, with Relationship Node"
|
const lexicalBlock = richTextField.locator('.lexical-block').nth(2) // third: "Block Node, with RichText Field, with Relationship Node"
|
||||||
await lexicalBlock.scrollIntoViewIfNeeded()
|
await lexicalBlock.scrollIntoViewIfNeeded()
|
||||||
await expect(lexicalBlock).toBeVisible()
|
await expect(lexicalBlock).toBeVisible()
|
||||||
|
|
||||||
@@ -388,7 +388,7 @@ describe('lexical', () => {
|
|||||||
await richTextField.scrollIntoViewIfNeeded()
|
await richTextField.scrollIntoViewIfNeeded()
|
||||||
await expect(richTextField).toBeVisible()
|
await expect(richTextField).toBeVisible()
|
||||||
|
|
||||||
const lexicalBlock = richTextField.locator('.lexical-block').nth(2) // third: "Block Node, with Blocks Field, With RichText Field, With Relationship Node"
|
const lexicalBlock = richTextField.locator('.lexical-block').nth(3) // third: "Block Node, with Blocks Field, With RichText Field, With Relationship Node"
|
||||||
await lexicalBlock.scrollIntoViewIfNeeded()
|
await lexicalBlock.scrollIntoViewIfNeeded()
|
||||||
await expect(lexicalBlock).toBeVisible()
|
await expect(lexicalBlock).toBeVisible()
|
||||||
|
|
||||||
@@ -441,7 +441,7 @@ describe('lexical', () => {
|
|||||||
).docs[0] as never
|
).docs[0] as never
|
||||||
|
|
||||||
const lexicalField: SerializedEditorState = lexicalDoc.lexicalWithBlocks
|
const lexicalField: SerializedEditorState = lexicalDoc.lexicalWithBlocks
|
||||||
const blockNode: SerializedBlockNode = lexicalField.root.children[4] as SerializedBlockNode
|
const blockNode: SerializedBlockNode = lexicalField.root.children[5] as SerializedBlockNode
|
||||||
const subBlocks = blockNode.fields.subBlocks
|
const subBlocks = blockNode.fields.subBlocks
|
||||||
|
|
||||||
expect(subBlocks).toHaveLength(2)
|
expect(subBlocks).toHaveLength(2)
|
||||||
@@ -459,9 +459,9 @@ describe('lexical', () => {
|
|||||||
await richTextField.scrollIntoViewIfNeeded()
|
await richTextField.scrollIntoViewIfNeeded()
|
||||||
await expect(richTextField).toBeVisible()
|
await expect(richTextField).toBeVisible()
|
||||||
|
|
||||||
const radioButtonBlock1 = richTextField.locator('.lexical-block').nth(4)
|
const radioButtonBlock1 = richTextField.locator('.lexical-block').nth(5)
|
||||||
|
|
||||||
const radioButtonBlock2 = richTextField.locator('.lexical-block').nth(5)
|
const radioButtonBlock2 = richTextField.locator('.lexical-block').nth(6)
|
||||||
await radioButtonBlock2.scrollIntoViewIfNeeded()
|
await radioButtonBlock2.scrollIntoViewIfNeeded()
|
||||||
await expect(radioButtonBlock1).toBeVisible()
|
await expect(radioButtonBlock1).toBeVisible()
|
||||||
await expect(radioButtonBlock2).toBeVisible()
|
await expect(radioButtonBlock2).toBeVisible()
|
||||||
@@ -507,8 +507,8 @@ describe('lexical', () => {
|
|||||||
).docs[0] as never
|
).docs[0] as never
|
||||||
|
|
||||||
const lexicalField: SerializedEditorState = lexicalDoc.lexicalWithBlocks
|
const lexicalField: SerializedEditorState = lexicalDoc.lexicalWithBlocks
|
||||||
const radio1: SerializedBlockNode = lexicalField.root.children[7] as SerializedBlockNode
|
const radio1: SerializedBlockNode = lexicalField.root.children[8] as SerializedBlockNode
|
||||||
const radio2: SerializedBlockNode = lexicalField.root.children[8] as SerializedBlockNode
|
const radio2: SerializedBlockNode = lexicalField.root.children[9] as SerializedBlockNode
|
||||||
|
|
||||||
expect(radio1.fields.radioButtons).toBe('option2')
|
expect(radio1.fields.radioButtons).toBe('option2')
|
||||||
expect(radio2.fields.radioButtons).toBe('option3')
|
expect(radio2.fields.radioButtons).toBe('option3')
|
||||||
@@ -534,7 +534,7 @@ describe('lexical', () => {
|
|||||||
|
|
||||||
await parentEditorParagraph.click() // Click works better than focus
|
await parentEditorParagraph.click() // Click works better than focus
|
||||||
|
|
||||||
const blockWithRichTextEditor = richTextField.locator('.lexical-block').nth(1) // third: "Block Node, with Blocks Field, With RichText Field, With Relationship Node"
|
const blockWithRichTextEditor = richTextField.locator('.lexical-block').nth(2) // third: "Block Node, with Blocks Field, With RichText Field, With Relationship Node"
|
||||||
await blockWithRichTextEditor.scrollIntoViewIfNeeded()
|
await blockWithRichTextEditor.scrollIntoViewIfNeeded()
|
||||||
await expect(blockWithRichTextEditor).toBeVisible()
|
await expect(blockWithRichTextEditor).toBeVisible()
|
||||||
|
|
||||||
@@ -567,7 +567,7 @@ describe('lexical', () => {
|
|||||||
await richTextField.scrollIntoViewIfNeeded()
|
await richTextField.scrollIntoViewIfNeeded()
|
||||||
await expect(richTextField).toBeVisible()
|
await expect(richTextField).toBeVisible()
|
||||||
|
|
||||||
const conditionalArrayBlock = richTextField.locator('.lexical-block').nth(6)
|
const conditionalArrayBlock = richTextField.locator('.lexical-block').nth(7)
|
||||||
|
|
||||||
await conditionalArrayBlock.scrollIntoViewIfNeeded()
|
await conditionalArrayBlock.scrollIntoViewIfNeeded()
|
||||||
await expect(conditionalArrayBlock).toBeVisible()
|
await expect(conditionalArrayBlock).toBeVisible()
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { lexicalMigrateDocData } from './collections/LexicalMigrate/data'
|
|||||||
import { richTextDocData } from './collections/RichText/data'
|
import { richTextDocData } from './collections/RichText/data'
|
||||||
import { generateLexicalRichText } from './collections/RichText/generateLexicalRichText'
|
import { generateLexicalRichText } from './collections/RichText/generateLexicalRichText'
|
||||||
import { textDoc } from './collections/Text/shared'
|
import { textDoc } from './collections/Text/shared'
|
||||||
|
import { uploadsDoc } from './collections/Upload/shared'
|
||||||
import { clearAndSeedEverything } from './seed'
|
import { clearAndSeedEverything } from './seed'
|
||||||
import {
|
import {
|
||||||
arrayFieldsSlug,
|
arrayFieldsSlug,
|
||||||
@@ -331,6 +332,73 @@ describe('Lexical', () => {
|
|||||||
expect(relationshipBlockNode.fields.rel.filename).toStrictEqual('payload.jpg')
|
expect(relationshipBlockNode.fields.rel.filename).toStrictEqual('payload.jpg')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should correctly populate polymorphic hasMany relationships in blocks with depth=0', async () => {
|
||||||
|
const lexicalDoc: LexicalField = (
|
||||||
|
await payload.find({
|
||||||
|
collection: lexicalFieldsSlug,
|
||||||
|
where: {
|
||||||
|
title: {
|
||||||
|
equals: lexicalDocData.title,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
depth: 0,
|
||||||
|
})
|
||||||
|
).docs[0] as never
|
||||||
|
|
||||||
|
const lexicalField: SerializedEditorState = lexicalDoc?.lexicalWithBlocks
|
||||||
|
|
||||||
|
const relationshipBlockNode: SerializedBlockNode = lexicalField.root
|
||||||
|
.children[3] as SerializedBlockNode
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Depth 0 population:
|
||||||
|
*/
|
||||||
|
expect(Object.keys(relationshipBlockNode.fields.rel[0])).toHaveLength(2)
|
||||||
|
expect(relationshipBlockNode.fields.rel[0].relationTo).toStrictEqual('text-fields')
|
||||||
|
expect(relationshipBlockNode.fields.rel[0].value).toStrictEqual(createdTextDocID)
|
||||||
|
|
||||||
|
expect(Object.keys(relationshipBlockNode.fields.rel[1])).toHaveLength(2)
|
||||||
|
expect(relationshipBlockNode.fields.rel[1].relationTo).toStrictEqual('uploads')
|
||||||
|
expect(relationshipBlockNode.fields.rel[1].value).toStrictEqual(createdJPGDocID)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should correctly populate polymorphic hasMany relationships in blocks with depth=1', async () => {
|
||||||
|
// Related issue: https://github.com/payloadcms/payload/issues/4277
|
||||||
|
const lexicalDoc: LexicalField = (
|
||||||
|
await payload.find({
|
||||||
|
collection: lexicalFieldsSlug,
|
||||||
|
where: {
|
||||||
|
title: {
|
||||||
|
equals: lexicalDocData.title,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
depth: 1,
|
||||||
|
})
|
||||||
|
).docs[0] as never
|
||||||
|
|
||||||
|
const lexicalField: SerializedEditorState = lexicalDoc?.lexicalWithBlocks
|
||||||
|
|
||||||
|
const relationshipBlockNode: SerializedBlockNode = lexicalField.root
|
||||||
|
.children[3] as SerializedBlockNode
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Depth 1 population:
|
||||||
|
*/
|
||||||
|
expect(Object.keys(relationshipBlockNode.fields.rel[0])).toHaveLength(2)
|
||||||
|
expect(relationshipBlockNode.fields.rel[0].relationTo).toStrictEqual('text-fields')
|
||||||
|
expect(relationshipBlockNode.fields.rel[0].value.id).toStrictEqual(createdTextDocID)
|
||||||
|
expect(relationshipBlockNode.fields.rel[0].value.text).toStrictEqual(textDoc.text)
|
||||||
|
expect(relationshipBlockNode.fields.rel[0].value.localizedText).toStrictEqual(
|
||||||
|
textDoc.localizedText,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(Object.keys(relationshipBlockNode.fields.rel[1])).toHaveLength(2)
|
||||||
|
expect(relationshipBlockNode.fields.rel[1].relationTo).toStrictEqual('uploads')
|
||||||
|
expect(relationshipBlockNode.fields.rel[1].value.id).toStrictEqual(createdJPGDocID)
|
||||||
|
expect(relationshipBlockNode.fields.rel[1].value.text).toStrictEqual(uploadsDoc.text)
|
||||||
|
expect(relationshipBlockNode.fields.rel[1].value.filename).toStrictEqual('payload.jpg')
|
||||||
|
})
|
||||||
|
|
||||||
it('should not populate relationship nodes inside of a sub-editor from a blocks node with 0 depth', async () => {
|
it('should not populate relationship nodes inside of a sub-editor from a blocks node with 0 depth', async () => {
|
||||||
const lexicalDoc: LexicalField = (
|
const lexicalDoc: LexicalField = (
|
||||||
await payload.find({
|
await payload.find({
|
||||||
@@ -347,7 +415,7 @@ describe('Lexical', () => {
|
|||||||
const lexicalField: SerializedEditorState = lexicalDoc?.lexicalWithBlocks
|
const lexicalField: SerializedEditorState = lexicalDoc?.lexicalWithBlocks
|
||||||
|
|
||||||
const subEditorBlockNode: SerializedBlockNode = lexicalField.root
|
const subEditorBlockNode: SerializedBlockNode = lexicalField.root
|
||||||
.children[3] as SerializedBlockNode
|
.children[4] as SerializedBlockNode
|
||||||
|
|
||||||
const subEditor: SerializedEditorState = subEditorBlockNode.fields.richText
|
const subEditor: SerializedEditorState = subEditorBlockNode.fields.richText
|
||||||
|
|
||||||
@@ -378,7 +446,7 @@ describe('Lexical', () => {
|
|||||||
const lexicalField: SerializedEditorState = lexicalDoc?.lexicalWithBlocks
|
const lexicalField: SerializedEditorState = lexicalDoc?.lexicalWithBlocks
|
||||||
|
|
||||||
const subEditorBlockNode: SerializedBlockNode = lexicalField.root
|
const subEditorBlockNode: SerializedBlockNode = lexicalField.root
|
||||||
.children[3] as SerializedBlockNode
|
.children[4] as SerializedBlockNode
|
||||||
|
|
||||||
const subEditor: SerializedEditorState = subEditorBlockNode.fields.richText
|
const subEditor: SerializedEditorState = subEditorBlockNode.fields.richText
|
||||||
|
|
||||||
@@ -425,7 +493,7 @@ describe('Lexical', () => {
|
|||||||
const lexicalField: SerializedEditorState = lexicalDoc?.lexicalWithBlocks
|
const lexicalField: SerializedEditorState = lexicalDoc?.lexicalWithBlocks
|
||||||
|
|
||||||
const subEditorBlockNode: SerializedBlockNode = lexicalField.root
|
const subEditorBlockNode: SerializedBlockNode = lexicalField.root
|
||||||
.children[3] as SerializedBlockNode
|
.children[4] as SerializedBlockNode
|
||||||
|
|
||||||
const subEditor: SerializedEditorState = subEditorBlockNode.fields.richText
|
const subEditor: SerializedEditorState = subEditorBlockNode.fields.richText
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user