chore: properly types cell components (#5474)

This commit is contained in:
Jacob Fletcher
2024-03-26 12:08:33 -04:00
committed by GitHub
parent 9c7e7ed8d4
commit 20b4585666
24 changed files with 93 additions and 67 deletions

View File

@@ -36,5 +36,9 @@ export const CreatedAtCell: React.FC<CreatedAtCellProps> = ({
if (globalSlug) to = `${admin}/globals/${globalSlug}/versions/${versionID}`
return <Link href={to}>{cellData && formatDate(cellData, dateFormat, i18n.language)}</Link>
return (
<Link href={to}>
{cellData && formatDate(cellData as Date | number | string, dateFormat, i18n.language)}
</Link>
)
}

View File

@@ -4,5 +4,5 @@ import React, { Fragment } from 'react'
export const IDCell: React.FC = () => {
const { cellData } = useTableCell()
return <Fragment>{cellData}</Fragment>
return <Fragment>{cellData as number | string}</Fragment>
}

View File

@@ -9,7 +9,9 @@ import type {
SelectField,
} from '../../fields/config/types.js'
export type CellProps = {
export type RowData = Record<string, any>
export type CellComponentProps = {
/**
* A custom component to override the default cell component. If this is not set, the React component will be
* taken from cellComponents based on the field type.
@@ -32,19 +34,18 @@ export type CellProps = {
onClick?: (args: {
cellData: unknown
collectionSlug: SanitizedCollectionConfig['slug']
rowData: Record<string, unknown>
rowData: RowData
}) => void
options?: SelectField['options']
relationTo?: RelationshipField['relationTo']
richTextComponentMap?: Map<string, React.ReactNode> // any should be MappedField
}
export type CellComponentProps<Data = unknown> = {
cellData: Data
export type DefaultCellComponentProps<T = any> = CellComponentProps & {
cellData: T
customCellContext?: {
collectionSlug?: SanitizedCollectionConfig['slug']
uploadConfig?: SanitizedCollectionConfig['upload']
}
richTextComponentMap?: Map<string, React.ReactNode>
rowData?: Record<string, unknown>
rowData: RowData
}

View File

@@ -1,5 +1,5 @@
export type { RichTextAdapter, RichTextFieldProps } from './RichText.js'
export type { CellComponentProps, CellProps } from './elements/Cell.js'
export type { CellComponentProps, DefaultCellComponentProps } from './elements/Cell.js'
export type { ConditionalDateProps } from './elements/DatePicker.js'
export type { DayPickerProps, SharedProps, TimePickerProps } from './elements/DatePicker.js'
export type { DefaultPreviewButtonProps } from './elements/PreviewButton.js'

View File

@@ -1,9 +1,9 @@
'use client'
import type { CellComponentProps } from 'payload/types'
import type { DefaultCellComponentProps } from 'payload/types'
import React from 'react'
export const RichTextCell: React.FC<CellComponentProps<any[]>> = ({ cellData }) => {
export const RichTextCell: React.FC<DefaultCellComponentProps<any[]>> = ({ cellData }) => {
const flattenedText = cellData?.map((i) => i?.children?.map((c) => c.text)).join(' ')
// Limiting the number of characters shown is done in a CSS rule

View File

@@ -1,12 +1,12 @@
'use client'
import type { CellComponentProps, CellProps } from 'payload/types'
import type { CellComponentProps, DefaultCellComponentProps } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
import { useTranslation } from '@payloadcms/ui/providers/Translation'
import React from 'react'
export interface ArrayCellProps extends CellComponentProps<Record<string, unknown>[]> {
labels: CellProps['labels']
export interface ArrayCellProps extends DefaultCellComponentProps<Record<string, unknown>[]> {
labels: CellComponentProps['labels']
}
export const ArrayCell: React.FC<ArrayCellProps> = ({ cellData, labels }) => {

View File

@@ -1,13 +1,13 @@
'use client'
import type { CellComponentProps, CellProps } from 'payload/types'
import type { CellComponentProps, DefaultCellComponentProps } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
import { useTranslation } from '@payloadcms/ui/providers/Translation'
import React from 'react'
export interface BlocksCellProps extends CellComponentProps<any> {
blocks: CellProps['blocks']
labels: CellProps['labels']
export interface BlocksCellProps extends DefaultCellComponentProps<any> {
blocks: CellComponentProps['blocks']
labels: CellComponentProps['labels']
}
export const BlocksCell: React.FC<BlocksCellProps> = ({ blocks, cellData, labels }) => {

View File

@@ -1,11 +1,11 @@
'use client'
import type { CellComponentProps } from 'payload/types'
import type { DefaultCellComponentProps } from 'payload/types'
import React from 'react'
import './index.scss'
export const CheckboxCell: React.FC<CellComponentProps<boolean>> = ({ cellData }) => (
export const CheckboxCell: React.FC<DefaultCellComponentProps<boolean>> = ({ cellData }) => (
<code className="bool-cell">
<span>{JSON.stringify(cellData)}</span>
</code>

View File

@@ -1,10 +1,10 @@
import type { CellComponentProps } from 'payload/types'
import type { DefaultCellComponentProps } from 'payload/types'
import React from 'react'
import './index.scss'
export interface CodeCellProps extends CellComponentProps<string> {
export interface CodeCellProps extends DefaultCellComponentProps<string> {
nowrap?: boolean
}

View File

@@ -1,16 +1,15 @@
'use client'
import type { CellComponentProps, CellProps } from 'payload/types'
import type { DefaultCellComponentProps } from 'payload/types'
import { useConfig } from '@payloadcms/ui/providers/Config'
import { useTranslation } from '@payloadcms/ui/providers/Translation'
import { formatDate } from '@payloadcms/ui/utilities/formatDate'
import React from 'react'
export interface DateCellProps extends CellComponentProps<string> {
dateDisplayFormat?: CellProps['dateDisplayFormat']
}
export const DateCell: React.FC<DateCellProps> = ({ cellData, dateDisplayFormat }) => {
export const DateCell: React.FC<DefaultCellComponentProps<Date | number | string>> = ({
cellData,
dateDisplayFormat,
}) => {
const {
admin: { dateFormat: dateFormatFromConfig },
} = useConfig()

View File

@@ -1,5 +1,5 @@
'use client'
import type { CellComponentProps } from 'payload/types'
import type { DefaultCellComponentProps } from 'payload/types'
import { Thumbnail } from '@payloadcms/ui/elements/Thumbnail'
import React from 'react'
@@ -8,7 +8,7 @@ import './index.scss'
const baseClass = 'file'
export interface FileCellProps extends CellComponentProps<any> {}
export interface FileCellProps extends DefaultCellComponentProps<any> {}
export const FileCell: React.FC<FileCellProps> = ({ cellData, customCellContext, rowData }) => {
const { collectionSlug, uploadConfig } = customCellContext

View File

@@ -1,11 +1,11 @@
'use client'
import type { CellComponentProps } from 'payload/types'
import type { DefaultCellComponentProps } from 'payload/types'
import React from 'react'
import './index.scss'
export const JSONCell: React.FC<CellComponentProps<string>> = ({ cellData }) => {
export const JSONCell: React.FC<DefaultCellComponentProps<string>> = ({ cellData }) => {
const textToShow = cellData.length > 100 ? `${cellData.substring(0, 100)}\u2026` : cellData
return (

View File

@@ -1,5 +1,5 @@
'use client'
import type { CellComponentProps, CellProps } from 'payload/types'
import type { CellComponentProps, DefaultCellComponentProps } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
import { useIntersect } from '@payloadcms/ui/hooks/useIntersect'
@@ -16,9 +16,9 @@ type Value = { relationTo: string; value: number | string }
const baseClass = 'relationship-cell'
const totalToShow = 3
export interface RelationshipCellProps extends CellComponentProps<any> {
label: CellProps['label']
relationTo: CellProps['relationTo']
export interface RelationshipCellProps extends DefaultCellComponentProps<any> {
label: CellComponentProps['label']
relationTo: CellComponentProps['relationTo']
}
export const RelationshipCell: React.FC<RelationshipCellProps> = ({

View File

@@ -1,13 +1,13 @@
'use client'
import type { CellComponentProps, CellProps, OptionObject } from 'payload/types'
import type { CellComponentProps, DefaultCellComponentProps, OptionObject } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
import { useTranslation } from '@payloadcms/ui/providers/Translation'
import { optionsAreObjects } from 'payload/types'
import React from 'react'
export interface SelectCellProps extends CellComponentProps<any> {
options: CellProps['options']
export interface SelectCellProps extends DefaultCellComponentProps<any> {
options: CellComponentProps['options']
}
export const SelectCell: React.FC<SelectCellProps> = ({ cellData, options }) => {

View File

@@ -1,9 +1,9 @@
'use client'
import type { CellComponentProps } from 'payload/types'
import type { DefaultCellComponentProps } from 'payload/types'
import React from 'react'
export const TextareaCell: React.FC<CellComponentProps<string>> = ({ cellData }) => {
export const TextareaCell: React.FC<DefaultCellComponentProps<string>> = ({ cellData }) => {
const textToShow = cellData?.length > 100 ? `${cellData.substr(0, 100)}\u2026` : cellData
return <span>{textToShow}</span>
}

View File

@@ -2,7 +2,7 @@
import LinkImport from 'next/link.js'
import React from 'react' // TODO: abstract this out to support all routers
import type { CellProps } from 'payload/types'
import type { CellComponentProps, DefaultCellComponentProps } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
import { TableCellProvider, useTableCell } from '@payloadcms/ui/elements/Table'
@@ -14,7 +14,7 @@ import { cellComponents } from './fields/index.js'
const Link = (LinkImport.default || LinkImport) as unknown as typeof LinkImport.default
export const DefaultCell: React.FC<CellProps> = (props) => {
export const DefaultCell: React.FC<CellComponentProps> = (props) => {
const {
name,
CellComponentOverride,
@@ -77,12 +77,12 @@ export const DefaultCell: React.FC<CellProps> = (props) => {
if (name === 'id') {
return (
<WrapElement {...wrapElementProps}>
<CodeCell cellData={`ID: ${cellData}`} nowrap />
<CodeCell cellData={`ID: ${cellData}`} name={name} nowrap rowData={rowData} />
</WrapElement>
)
}
const DefaultCellComponent = cellComponents[fieldType]
const DefaultCellComponent: React.FC<DefaultCellComponentProps> = cellComponents[fieldType]
let CellComponent: React.ReactNode =
cellData &&

View File

@@ -1,27 +1,27 @@
'use client'
import type { CellComponentProps, CellProps } from 'payload/types'
import type { CellComponentProps, DefaultCellComponentProps } from 'payload/types'
import React from 'react'
export type ITableCellContext = {
cellData: any
cellProps?: Partial<CellProps>
cellData: DefaultCellComponentProps['cellData']
cellProps?: Partial<CellComponentProps>
columnIndex?: number
customCellContext: CellComponentProps['customCellContext']
richTextComponentMap?: CellComponentProps['richTextComponentMap']
rowData: any
customCellContext: DefaultCellComponentProps['customCellContext']
richTextComponentMap?: DefaultCellComponentProps['richTextComponentMap']
rowData: DefaultCellComponentProps['rowData']
}
const TableCellContext = React.createContext<ITableCellContext>({} as ITableCellContext)
export const TableCellProvider: React.FC<{
cellData?: any
cellProps?: Partial<CellProps>
cellData?: DefaultCellComponentProps['cellData']
cellProps?: Partial<CellComponentProps>
children: React.ReactNode
columnIndex?: number
customCellContext?: CellComponentProps['customCellContext']
richTextComponentMap?: CellComponentProps['richTextComponentMap']
rowData?: any
customCellContext?: DefaultCellComponentProps['customCellContext']
richTextComponentMap?: DefaultCellComponentProps['richTextComponentMap']
rowData?: DefaultCellComponentProps['rowData']
}> = (props) => {
const {
cellData,

View File

@@ -1,5 +1,5 @@
'use client'
import type { CellProps, FieldBase } from 'payload/types'
import type { CellComponentProps, FieldBase } from 'payload/types'
import React from 'react'
@@ -11,12 +11,14 @@ import { useTableColumns } from '../TableColumns/index.js'
import { TableCellProvider } from './TableCellProvider/index.js'
import './index.scss'
export { TableCellProvider }
const baseClass = 'table'
export type Column = {
accessor: string
active: boolean
cellProps?: Partial<CellProps>
cellProps?: Partial<CellComponentProps>
components: {
Cell: React.ReactNode
Heading: React.ReactNode
@@ -28,7 +30,7 @@ export type Column = {
export type Props = {
columns?: Column[]
customCellContext?: Record<string, unknown>
data: unknown[]
data: Record<string, unknown>[]
fieldMap: FieldMap
}

View File

@@ -1,5 +1,5 @@
import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
import { type CellProps, type SanitizedCollectionConfig } from 'payload/types'
import { type CellComponentProps, type SanitizedCollectionConfig } from 'payload/types'
import React from 'react'
import type { FieldMap, MappedField } from '../../providers/ComponentMap/buildComponentMap/types.js'
@@ -15,7 +15,7 @@ import { DefaultCell } from '../Table/DefaultCell/index.js'
const fieldIsPresentationalOnly = (field: MappedField): boolean => field.type === 'ui'
export const buildColumns = (args: {
cellProps: Partial<CellProps>[]
cellProps: Partial<CellComponentProps>[]
columnPreferences: ColumnPreferences
defaultColumns?: string[]
enableRowSelections: boolean

View File

@@ -1,6 +1,6 @@
'use client'
import type { SanitizedCollectionConfig } from 'payload/types'
import type { CellProps } from 'payload/types'
import type { CellComponentProps } from 'payload/types'
import React, { createContext, useCallback, useContext, useEffect, useReducer, useRef } from 'react'
@@ -31,7 +31,7 @@ export type ListPreferences = {
}
type Props = {
cellProps?: Partial<CellProps>[]
cellProps?: Partial<CellComponentProps>[]
children: React.ReactNode
collectionSlug: string
enableRowSelections?: boolean

View File

@@ -1,6 +1,6 @@
import type { FieldDescriptionProps } from '@payloadcms/ui/forms/FieldDescription'
import type {
CellProps,
CellComponentProps,
Field,
FieldWithPath,
LabelProps,
@@ -189,7 +189,7 @@ export const mapFields = (args: {
let fieldComponentProps: FieldComponentProps
const cellComponentProps: CellProps = {
const cellComponentProps: CellComponentProps = {
name: 'name' in field ? field.name : undefined,
fieldType: field.type,
isFieldAffectingData,

View File

@@ -2,7 +2,7 @@ import type { HiddenInputFieldProps } from '@payloadcms/ui/fields/HiddenInput'
import type { FieldTypes } from 'payload/config'
import type {
BlockField,
CellProps,
CellComponentProps,
SanitizedCollectionConfig,
SanitizedGlobalConfig,
TabsField,
@@ -67,7 +67,7 @@ export type FieldComponentProps =
export type MappedField = {
CustomCell?: React.ReactNode
CustomField?: React.ReactNode
cellComponentProps: CellProps
cellComponentProps: CellComponentProps
disableBulkEdit?: boolean
disabled?: boolean
fieldComponentProps: FieldComponentProps

View File

@@ -2,6 +2,7 @@ import type { CollectionConfig } from 'payload/types'
import { slateEditor } from '@payloadcms/richtext-slate'
import { CustomCell } from '../components/CustomCell/index.js'
import { DemoUIFieldCell } from '../components/DemoUIField/Cell.js'
import { DemoUIField } from '../components/DemoUIField/Field.js'
import {
@@ -82,6 +83,15 @@ export const Posts: CollectionConfig = {
},
relationTo: 'posts',
},
{
name: 'customCell',
type: 'text',
admin: {
components: {
Cell: CustomCell,
},
},
},
{
name: 'sidebarField',
type: 'text',

View File

@@ -0,0 +1,10 @@
'use client'
import type { CellComponentProps } from 'payload/types'
import { useTableCell } from '@payloadcms/ui/elements/Table'
import React from 'react'
export const CustomCell: React.FC<CellComponentProps> = (props) => {
const context = useTableCell()
return <div>{`Custom cell: ${context.cellData || 'No data'}`}</div>
}