chore(richtext-lexical): improve types of UploadData (#10982)

One step closer to being able to remove `noUncheckedIndexedAccess` in
`packages/richtext-lexical/tsconfig.json`.

I'm introducing UploadData_P4 which is a more precise version of
UploadData. I'm doing it as a different type because there's a chance
it'll be a breaking change for some users.

UploadData is used in many places, but I'm currently replacing it only
in
`packages/richtext-lexical/src/exports/react/components/RichText/converter/converters/upload.tsx`,
because in the other files it's too rooted to other types like
UploadNode.
This commit is contained in:
Germán Jabloñski
2025-02-11 16:24:05 -03:00
committed by GitHub
parent 3098f35537
commit 002e921ede
3 changed files with 50 additions and 26 deletions

View File

@@ -12,6 +12,16 @@ export type FileSize = {
width: null | number width: null | number
} }
// TODO: deprecate in Payload v4.
/**
* FileSizeImproved is a more precise type, and will replace FileSize in Payload v4.
* This type is for internal use only as it will be deprecated in the future.
* @internal
*/
export type FileSizeImproved = {
url: null | string
} & FileSize
export type FileSizes = { export type FileSizes = {
[size: string]: FileSize [size: string]: FileSize
} }

View File

@@ -1,23 +1,25 @@
import type { FileData, FileSize, TypeWithID } from 'payload' import type { FileSizeImproved } from 'payload'
import type { UploadDataImproved } from '../../../../../../features/upload/server/nodes/UploadNode.js'
import type { SerializedUploadNode } from '../../../../../../nodeTypes.js' import type { SerializedUploadNode } from '../../../../../../nodeTypes.js'
import type { JSXConverters } from '../types.js' import type { JSXConverters } from '../types.js'
export const UploadJSXConverter: JSXConverters<SerializedUploadNode> = { export const UploadJSXConverter: JSXConverters<SerializedUploadNode> = {
upload: ({ node }) => { upload: ({ node }) => {
const uploadDocument: { // TO-DO (v4): SerializedUploadNode should use UploadData_P4
value?: FileData & TypeWithID const uploadDocument = node as UploadDataImproved
} = node as any if (typeof uploadDocument.value !== 'object') {
return null
const url = uploadDocument?.value?.url }
const url = uploadDocument.value.url
/** /**
* If the upload is not an image, return a link to the upload * If the upload is not an image, return a link to the upload
*/ */
if (!uploadDocument?.value?.mimeType?.startsWith('image')) { if (!uploadDocument.value.mimeType.startsWith('image')) {
return ( return (
<a href={url} rel="noopener noreferrer"> <a href={url} rel="noopener noreferrer">
{uploadDocument.value?.filename} {uploadDocument.value.filename}
</a> </a>
) )
} }
@@ -25,13 +27,13 @@ export const UploadJSXConverter: JSXConverters<SerializedUploadNode> = {
/** /**
* If the upload is a simple image with no different sizes, return a simple img tag * If the upload is a simple image with no different sizes, return a simple img tag
*/ */
if (!uploadDocument?.value?.sizes || !Object.keys(uploadDocument?.value?.sizes).length) { if (!Object.keys(uploadDocument.value.sizes).length) {
return ( return (
<img <img
alt={uploadDocument?.value?.filename} alt={uploadDocument.value.filename}
height={uploadDocument?.value?.height} height={uploadDocument.value.height}
src={url} src={url}
width={uploadDocument?.value?.width} width={uploadDocument.value.width}
/> />
) )
} }
@@ -42,13 +44,12 @@ export const UploadJSXConverter: JSXConverters<SerializedUploadNode> = {
const pictureJSX: React.ReactNode[] = [] const pictureJSX: React.ReactNode[] = []
// Iterate through each size in the data.sizes object // Iterate through each size in the data.sizes object
for (const size in uploadDocument.value?.sizes) { for (const size in uploadDocument.value.sizes) {
const imageSize: { const imageSize = uploadDocument.value.sizes[size] as FileSizeImproved
url?: string
} & FileSize = uploadDocument.value?.sizes[size]
// Skip if any property of the size object is null // Skip if any property of the size object is null
if ( if (
!imageSize ||
!imageSize.width || !imageSize.width ||
!imageSize.height || !imageSize.height ||
!imageSize.mimeType || !imageSize.mimeType ||

View File

@@ -8,7 +8,7 @@ import type {
NodeKey, NodeKey,
Spread, Spread,
} from 'lexical' } from 'lexical'
import type { CollectionSlug, DataFromCollectionSlug, JsonObject } from 'payload' import type { FileData, JsonObject, TypedCollection, TypeWithID } from 'payload'
import type { JSX } from 'react' import type { JSX } from 'react'
import { DecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode.js' import { DecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode.js'
@@ -17,15 +17,28 @@ import { $applyNodeReplacement } from 'lexical'
import * as React from 'react' import * as React from 'react'
export type UploadData<TUploadExtraFieldsData extends JsonObject = JsonObject> = { export type UploadData<TUploadExtraFieldsData extends JsonObject = JsonObject> = {
[TCollectionSlug in CollectionSlug]: { fields: TUploadExtraFieldsData
fields: TUploadExtraFieldsData /** Every lexical node that has sub-fields needs to have a unique ID. This is the ID of this upload node, not the ID of the linked upload document */
// Every lexical node that has sub-fields needs to have a unique ID. This is the ID of this upload node, not the ID of the linked upload document id: string
id: string relationTo: string
relationTo: TCollectionSlug /** Value can be just the document ID, or the full, populated document */
// Value can be just the document ID, or the full, populated document value: number | string | TypedCollection
value: DataFromCollectionSlug<TCollectionSlug> | number | string }
}
}[CollectionSlug] // TODO: deprecate in Payload v4.
/**
* UploadDataImproved is a more precise type, and will replace UploadData in Payload v4.
* This type is for internal use only as it will be deprecated in the future.
* @internal
*/
export type UploadDataImproved<TUploadExtraFieldsData extends JsonObject = JsonObject> = {
fields: TUploadExtraFieldsData
// Every lexical node that has sub-fields needs to have a unique ID. This is the ID of this upload node, not the ID of the linked upload document
id: string
relationTo: string
// Value can be just the document ID, or the full, populated document
value: (FileData & TypeWithID) | number | string
}
export function isGoogleDocCheckboxImg(img: HTMLImageElement): boolean { export function isGoogleDocCheckboxImg(img: HTMLImageElement): boolean {
return ( return (