Skip to content

Commit

Permalink
feat(richtext-lexical)!: add validation to link and upload nodes
Browse files Browse the repository at this point in the history
BREAKING: this will now display errors if you're previously had invalid link or upload fields data - for example if you have a required field added to an uploads node and did not provide a value to it every time you've added an upload node
  • Loading branch information
AlessioGr committed May 1, 2024
1 parent e8983ab commit 8829fba
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { buildStateFromSchema } from '@payloadcms/ui/forms/buildStateFromSchema'

import type { NodeValidation } from '../types.js'
import type { BlocksFeatureProps } from './feature.server.js'
import type { SerializedBlockNode } from './nodes/BlocksNode.js'
import type { BlockFields, SerializedBlockNode } from './nodes/BlocksNode.js'

export const blockValidationHOC = (
props: BlocksFeatureProps,
): NodeValidation<SerializedBlockNode> => {
return async ({ node, validation }) => {
const blockFieldData = node.fields
const blockFieldData = node.fields ?? ({} as BlockFields)

const {
options: { id, operation, preferences, req },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { AutoLinkNode } from './nodes/AutoLinkNode.js'
import { LinkNode } from './nodes/LinkNode.js'
import { transformExtraFields } from './plugins/floatingLinkEditor/utilities.js'
import { linkPopulationPromiseHOC } from './populationPromise.js'
import { linkValidation } from './validate.js'

export type ExclusiveLinkCollectionsProps =
| {
Expand Down Expand Up @@ -143,6 +144,7 @@ export const LinkFeature: FeatureProviderProviderServer<LinkFeatureServerProps,
},
node: AutoLinkNode,
populationPromises: [linkPopulationPromiseHOC(props)],
validations: [linkValidation(props)],
}),
createNode({
converters: {
Expand Down Expand Up @@ -172,6 +174,7 @@ export const LinkFeature: FeatureProviderProviderServer<LinkFeatureServerProps,
},
node: LinkNode,
populationPromises: [linkPopulationPromiseHOC(props)],
validations: [linkValidation(props)],
}),
],
serverFeatureProps: props,
Expand Down
51 changes: 51 additions & 0 deletions packages/richtext-lexical/src/field/features/link/validate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { FieldWithRichTextRequiredEditor } from 'payload/types'

import { buildStateFromSchema } from '@payloadcms/ui/forms/buildStateFromSchema'

import type { NodeValidation } from '../types.js'
import type { LinkFeatureServerProps } from './feature.server.js'
import type { SerializedAutoLinkNode, SerializedLinkNode } from './nodes/types.js'

export const linkValidation = (
props: LinkFeatureServerProps,
// eslint-disable-next-line @typescript-eslint/no-duplicate-type-constituents
): NodeValidation<SerializedAutoLinkNode | SerializedLinkNode> => {
return async ({
node,
validation: {
options: { id, operation, preferences, req },
},
}) => {
/**
* Run buildStateFromSchema as that properly validates link fields and link sub-fields
*/

const data = {
...node.fields,
text: 'ignored',
}

const result = await buildStateFromSchema({
id,
data,
fieldSchema: props.fields as FieldWithRichTextRequiredEditor[], // Sanitized in feature.server.ts
operation: operation === 'create' || operation === 'update' ? operation : 'update',
preferences,
req,
siblingData: data,
})

let errorPaths = []
for (const fieldKey in result) {
if (result[fieldKey].errorPaths) {
errorPaths = errorPaths.concat(result[fieldKey].errorPaths)
}
}

if (errorPaths.length) {
return 'Link fields validation failed: ' + errorPaths.join(', ')
}

return true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ export const UploadFeature: FeatureProviderProviderServer<
},
node: UploadNode,
populationPromises: [uploadPopulationPromiseHOC(props)],
validations: [uploadValidation()],
validations: [uploadValidation(props)],
}),
],
serverFeatureProps: props,
Expand Down
55 changes: 44 additions & 11 deletions packages/richtext-lexical/src/field/features/upload/validate.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,63 @@
import { buildStateFromSchema } from '@payloadcms/ui/forms/buildStateFromSchema'
import { isValidID } from 'payload/utilities'

import type { NodeValidation } from '../types.js'
import type { UploadFeatureProps } from './feature.server.js'
import type { SerializedUploadNode } from './nodes/UploadNode.js'

import { CAN_USE_DOM } from '../../lexical/utils/canUseDOM.js'

export const uploadValidation = (): NodeValidation<SerializedUploadNode> => {
return ({
export const uploadValidation = (
props: UploadFeatureProps,
): NodeValidation<SerializedUploadNode> => {
return async ({
node,
validation: {
options: {
id,
operation,
preferences,
req,
req: { payload, t },
},
},
}) => {
if (!CAN_USE_DOM) {
const idType = payload.collections[node.relationTo].customIDType || payload.db.defaultIDType
// @ts-expect-error
const id = node?.value?.id || node?.value // for backwards-compatibility
const idType = payload.collections[node.relationTo].customIDType || payload.db.defaultIDType
// @ts-expect-error
const nodeID = node?.value?.id || node?.value // for backwards-compatibility

if (!isValidID(nodeID, idType)) {
return t('validation:validUploadID')
}

if (Object.keys(props?.collections).length === 0) {
return true
}

if (!isValidID(id, idType)) {
return t('validation:validUploadID')
const collection = props?.collections[node.relationTo]

if (!collection.fields?.length) {
return true
}

const result = await buildStateFromSchema({
id,
data: node?.fields ?? {},
fieldSchema: collection.fields,
operation: operation === 'create' || operation === 'update' ? operation : 'update',
preferences,
req,
siblingData: node?.fields ?? {},
})

let errorPaths = []
for (const fieldKey in result) {
if (result[fieldKey].errorPaths) {
errorPaths = errorPaths.concat(result[fieldKey].errorPaths)
}
}

// TODO: validate upload collection fields
if (errorPaths.length) {
return 'Block validation failed: ' + errorPaths.join(', ')
}

return true
}
Expand Down

0 comments on commit 8829fba

Please sign in to comment.