feat(ui): provides payload as prop to all custom server components (#5775)

This commit is contained in:
Jacob Fletcher
2024-04-11 11:16:15 -04:00
committed by GitHub
parent d0869d9087
commit 1275c70187
35 changed files with 324 additions and 133 deletions

View File

@@ -12,6 +12,7 @@ import { createClientConfig } from 'payload/config'
import React from 'react' import React from 'react'
import 'react-toastify/dist/ReactToastify.css' import 'react-toastify/dist/ReactToastify.css'
import { getPayloadHMR } from '../../utilities/getPayloadHMR.js'
import { getRequestLanguage } from '../../utilities/getRequestLanguage.js' import { getRequestLanguage } from '../../utilities/getRequestLanguage.js'
import { DefaultEditView } from '../../views/Edit/Default/index.js' import { DefaultEditView } from '../../views/Edit/Default/index.js'
import { DefaultListView } from '../../views/List/Default/index.js' import { DefaultListView } from '../../views/List/Default/index.js'
@@ -39,6 +40,7 @@ export const RootLayout = async ({
headers, headers,
}) })
const payload = await getPayloadHMR({ config })
const i18n = await initI18n({ config: config.i18n, context: 'client', language: languageCode }) const i18n = await initI18n({ config: config.i18n, context: 'client', language: languageCode })
const clientConfig = await createClientConfig({ config, t: i18n.t }) const clientConfig = await createClientConfig({ config, t: i18n.t })
@@ -76,6 +78,7 @@ export const RootLayout = async ({
children, children,
config, config,
i18n, i18n,
payload,
}) })
return ( return (

View File

@@ -1,9 +1,10 @@
import type { Field } from 'payload/types' import type { Field, WithServerSideProps as WithServerSidePropsType } from 'payload/types'
import type { AdminViewProps } from 'payload/types' import type { AdminViewProps } from 'payload/types'
import { Form } from '@payloadcms/ui/forms/Form' import { Form } from '@payloadcms/ui/forms/Form'
import { FormSubmit } from '@payloadcms/ui/forms/Submit' import { FormSubmit } from '@payloadcms/ui/forms/Submit'
import { buildStateFromSchema } from '@payloadcms/ui/forms/buildStateFromSchema' import { buildStateFromSchema } from '@payloadcms/ui/forms/buildStateFromSchema'
import { WithServerSideProps as WithServerSidePropsGeneric } from '@payloadcms/ui/providers/ComponentMap'
import { mapFields } from '@payloadcms/ui/utilities/buildComponentMap' import { mapFields } from '@payloadcms/ui/utilities/buildComponentMap'
import React from 'react' import React from 'react'
@@ -17,6 +18,7 @@ export const CreateFirstUserView: React.FC<AdminViewProps> = async ({ initPageRe
req, req,
req: { req: {
i18n, i18n,
payload,
payload: { payload: {
config, config,
config: { config: {
@@ -49,7 +51,12 @@ export const CreateFirstUserView: React.FC<AdminViewProps> = async ({ initPageRe
}, },
] ]
const WithServerSideProps: WithServerSidePropsType = ({ Component, ...rest }) => {
return <WithServerSidePropsGeneric Component={Component} payload={payload} {...rest} />
}
const createFirstUserFieldMap = mapFields({ const createFirstUserFieldMap = mapFields({
WithServerSideProps,
config, config,
fieldSchema: fields, fieldSchema: fields,
i18n, i18n,

View File

@@ -4,6 +4,7 @@ import type { JSONSchema4 } from 'json-schema'
import type { SanitizedConfig } from '../config/types.js' import type { SanitizedConfig } from '../config/types.js'
import type { Field, RichTextField, Validate } from '../fields/config/types.js' import type { Field, RichTextField, Validate } from '../fields/config/types.js'
import type { PayloadRequest, RequestContext } from '../types/index.js' import type { PayloadRequest, RequestContext } from '../types/index.js'
import type { WithServerSideProps } from './elements/WithServerSideProps.js'
export type RichTextFieldProps< export type RichTextFieldProps<
Value extends object, Value extends object,
@@ -28,6 +29,7 @@ type RichTextAdapterBase<
siblingDoc: Record<string, unknown> siblingDoc: Record<string, unknown>
}) => Promise<void> | null }) => Promise<void> | null
generateComponentMap: (args: { generateComponentMap: (args: {
WithServerSideProps: WithServerSideProps
config: SanitizedConfig config: SanitizedConfig
i18n: I18n i18n: I18n
schemaPath: string schemaPath: string

View File

@@ -1 +1,3 @@
export type CustomPreviewButton = React.ComponentType import type { CustomComponent } from '../../config/types.js'
export type CustomPreviewButton = CustomComponent

View File

@@ -1 +1,3 @@
export type CustomPublishButton = React.ComponentType import type { CustomComponent } from '../../config/types.js'
export type CustomPublishButton = CustomComponent

View File

@@ -1 +1,3 @@
export type CustomSaveButton = React.ComponentType import type { CustomComponent } from '../../config/types.js'
export type CustomSaveButton = CustomComponent

View File

@@ -1 +1,3 @@
export type CustomSaveDraftButton = React.ComponentType import type { CustomComponent } from '../../config/types.js'
export type CustomSaveDraftButton = CustomComponent

View File

@@ -0,0 +1,4 @@
export type WithServerSideProps = (args: {
[key: string]: any
Component: React.ComponentType<any>
}) => React.ReactNode

View File

@@ -1,10 +1,11 @@
import type React from 'react' import type React from 'react'
import type { LabelFunction } from '../../config/types.js' import type { CustomComponent, LabelFunction } from '../../config/types.js'
import type { Payload } from '../../index.js'
export type DescriptionFunction = LabelFunction export type DescriptionFunction = LabelFunction
export type DescriptionComponent = React.ComponentType<FieldDescriptionProps> export type DescriptionComponent = CustomComponent<FieldDescriptionProps>
export type Description = export type Description =
| DescriptionComponent | DescriptionComponent
@@ -17,4 +18,5 @@ export type FieldDescriptionProps = {
className?: string className?: string
description?: Record<string, string> | string description?: Record<string, string> | string
marginPlacement?: 'bottom' | 'top' marginPlacement?: 'bottom' | 'top'
payload?: Payload
} }

View File

@@ -1,3 +1,5 @@
export type RowLabelComponent = React.ComponentType import type { CustomComponent } from '../../config/types.js'
export type RowLabelComponent = CustomComponent
export type RowLabel = Record<string, string> | RowLabelComponent | string export type RowLabel = Record<string, string> | RowLabelComponent | string

View File

@@ -13,6 +13,7 @@ export type {
DocumentTabConfig, DocumentTabConfig,
DocumentTabProps, DocumentTabProps,
} from './elements/Tab.js' } from './elements/Tab.js'
export type { WithServerSideProps } from './elements/WithServerSideProps.js'
export type { ErrorProps } from './forms/Error.js' export type { ErrorProps } from './forms/Error.js'
export type { export type {
Description, Description,

View File

@@ -10,6 +10,7 @@ import type {
import type { Auth, ClientUser, IncomingAuthType } from '../../auth/types.js' import type { Auth, ClientUser, IncomingAuthType } from '../../auth/types.js'
import type { import type {
Access, Access,
CustomComponent,
EditConfig, EditConfig,
Endpoint, Endpoint,
EntityDescription, EntityDescription,
@@ -201,10 +202,10 @@ export type CollectionAdminOptions = {
* Custom admin components * Custom admin components
*/ */
components?: { components?: {
AfterList?: React.ComponentType<any>[] AfterList?: CustomComponent[]
AfterListTable?: React.ComponentType<any>[] AfterListTable?: CustomComponent[]
BeforeList?: React.ComponentType<any>[] BeforeList?: CustomComponent[]
BeforeListTable?: React.ComponentType<any>[] BeforeListTable?: CustomComponent[]
/** /**
* Components within the edit view * Components within the edit view
*/ */
@@ -239,7 +240,7 @@ export type CollectionAdminOptions = {
List?: List?:
| { | {
Component?: React.ComponentType<any> Component?: React.ComponentType<any>
actions?: React.ComponentType<any>[] actions?: CustomComponent[]
} }
| React.ComponentType<any> | React.ComponentType<any>
} }

View File

@@ -279,7 +279,7 @@ export type EditViewConfig =
path: string path: string
} }
| { | {
actions?: React.ComponentType<any>[] actions?: CustomComponent[]
} }
/** /**
@@ -291,6 +291,14 @@ export type EditViewConfig =
*/ */
export type EditView = EditViewComponent | EditViewConfig export type EditView = EditViewComponent | EditViewConfig
export type ServerProps = {
payload: Payload
}
export const serverProps: (keyof ServerProps)[] = ['payload']
export type CustomComponent<T extends any = any> = React.ComponentType<T & ServerProps>
export type Locale = { export type Locale = {
/** /**
* value of supported locale * value of supported locale
@@ -420,46 +428,46 @@ export type Config = {
/** /**
* Replace the navigation with a custom component * Replace the navigation with a custom component
*/ */
Nav?: React.ComponentType<any> Nav?: CustomComponent
/** /**
* Add custom components to the top right of the Admin Panel * Add custom components to the top right of the Admin Panel
*/ */
actions?: React.ComponentType<any>[] actions?: CustomComponent[]
/** /**
* Add custom components after the collection overview * Add custom components after the collection overview
*/ */
afterDashboard?: React.ComponentType<any>[] afterDashboard?: CustomComponent[]
/** /**
* Add custom components after the email/password field * Add custom components after the email/password field
*/ */
afterLogin?: React.ComponentType<any>[] afterLogin?: CustomComponent[]
/** /**
* Add custom components after the navigation links * Add custom components after the navigation links
*/ */
afterNavLinks?: React.ComponentType<any>[] afterNavLinks?: CustomComponent[]
/** /**
* Add custom components before the collection overview * Add custom components before the collection overview
*/ */
beforeDashboard?: React.ComponentType<any>[] beforeDashboard?: CustomComponent[]
/** /**
* Add custom components before the email/password field * Add custom components before the email/password field
*/ */
beforeLogin?: React.ComponentType<any>[] beforeLogin?: CustomComponent[]
/** /**
* Add custom components before the navigation links * Add custom components before the navigation links
*/ */
beforeNavLinks?: React.ComponentType<any>[] beforeNavLinks?: CustomComponent[]
/** Replace graphical components */ /** Replace graphical components */
graphics?: { graphics?: {
/** Replace the icon in the navigation */ /** Replace the icon in the navigation */
Icon?: React.ComponentType<any> Icon?: CustomComponent
/** Replace the logo on the login page */ /** Replace the logo on the login page */
Logo?: React.ComponentType<any> Logo?: CustomComponent
} }
/** Replace logout related components */ /** Replace logout related components */
logout?: { logout?: {
/** Replace the logout button */ /** Replace the logout button */
Button?: React.ComponentType<any> Button?: CustomComponent
} }
/** /**
* Wrap the admin dashboard in custom context providers * Wrap the admin dashboard in custom context providers
@@ -716,7 +724,7 @@ export type EditConfig =
) )
| EditViewComponent | EditViewComponent
export type EntityDescriptionComponent = React.ComponentType<any> export type EntityDescriptionComponent = CustomComponent
export type EntityDescriptionFunction = () => string export type EntityDescriptionFunction = () => string

View File

@@ -34,7 +34,12 @@ export { isEntityHidden } from '../utilities/isEntityHidden.js'
export { isNumber } from '../utilities/isNumber.js' export { isNumber } from '../utilities/isNumber.js'
export { isPlainObject } from '../utilities/isPlainObject.js' export { isPlainObject } from '../utilities/isPlainObject.js'
export { isPlainFunction, isReactComponent } from '../utilities/isReactComponent.js' export {
isPlainFunction,
isReactClientComponent,
isReactComponent,
isReactServerComponent,
} from '../utilities/isReactComponent.js'
export { isValidID } from '../utilities/isValidID.js' export { isValidID } from '../utilities/isValidID.js'
export { default as isolateObjectProperty } from '../utilities/isolateObjectProperty.js' export { default as isolateObjectProperty } from '../utilities/isolateObjectProperty.js'

View File

@@ -17,7 +17,7 @@ import type {
} from '../../admin/types.js' } from '../../admin/types.js'
import type { User } from '../../auth/index.js' import type { User } from '../../auth/index.js'
import type { SanitizedCollectionConfig, TypeWithID } from '../../collections/config/types.js' import type { SanitizedCollectionConfig, TypeWithID } from '../../collections/config/types.js'
import type { LabelFunction } from '../../config/types.js' import type { CustomComponent, LabelFunction } from '../../config/types.js'
import type { DBIdentifierName } from '../../database/types.js' import type { DBIdentifierName } from '../../database/types.js'
import type { SanitizedGlobalConfig } from '../../globals/config/types.js' import type { SanitizedGlobalConfig } from '../../globals/config/types.js'
import type { Operation, PayloadRequest, RequestContext, Where } from '../../types/index.js' import type { Operation, PayloadRequest, RequestContext, Where } from '../../types/index.js'
@@ -115,8 +115,8 @@ export type FilterOptions<T = any> =
type Admin = { type Admin = {
className?: string className?: string
components?: { components?: {
Cell?: React.ComponentType<any> Cell?: CustomComponent
Field?: React.ComponentType<any> Field?: CustomComponent
Filter?: React.ComponentType<any> Filter?: React.ComponentType<any>
} }
/** /**
@@ -208,10 +208,10 @@ export type NumberField = FieldBase & {
/** Set this property to a string that will be used for browser autocomplete. */ /** Set this property to a string that will be used for browser autocomplete. */
autoComplete?: string autoComplete?: string
components?: { components?: {
Error?: React.ComponentType<ErrorProps> Error?: CustomComponent<ErrorProps>
Label?: React.ComponentType<LabelProps> Label?: CustomComponent<LabelProps>
afterInput?: React.ComponentType<any>[] afterInput?: CustomComponent[]
beforeInput?: React.ComponentType<any>[] beforeInput?: CustomComponent[]
} }
/** Set this property to define a placeholder string for the field. */ /** Set this property to define a placeholder string for the field. */
placeholder?: Record<string, string> | string placeholder?: Record<string, string> | string
@@ -246,10 +246,10 @@ export type TextField = FieldBase & {
admin?: Admin & { admin?: Admin & {
autoComplete?: string autoComplete?: string
components?: { components?: {
Error?: React.ComponentType<ErrorProps> Error?: CustomComponent<ErrorProps>
Label?: React.ComponentType<LabelProps> Label?: CustomComponent<LabelProps>
afterInput?: React.ComponentType<any>[] afterInput?: CustomComponent[]
beforeInput?: React.ComponentType<any>[] beforeInput?: CustomComponent[]
} }
placeholder?: Record<string, string> | string placeholder?: Record<string, string> | string
rtl?: boolean rtl?: boolean
@@ -280,10 +280,10 @@ export type EmailField = FieldBase & {
admin?: Admin & { admin?: Admin & {
autoComplete?: string autoComplete?: string
components?: { components?: {
Error?: React.ComponentType<ErrorProps> Error?: CustomComponent<ErrorProps>
Label?: React.ComponentType<LabelProps> Label?: CustomComponent<LabelProps>
afterInput?: React.ComponentType<any>[] afterInput?: CustomComponent[]
beforeInput?: React.ComponentType<any>[] beforeInput?: CustomComponent[]
} }
placeholder?: Record<string, string> | string placeholder?: Record<string, string> | string
} }
@@ -293,10 +293,10 @@ export type EmailField = FieldBase & {
export type TextareaField = FieldBase & { export type TextareaField = FieldBase & {
admin?: Admin & { admin?: Admin & {
components?: { components?: {
Error?: React.ComponentType<ErrorProps> Error?: CustomComponent<ErrorProps>
Label?: React.ComponentType<LabelProps> Label?: CustomComponent<LabelProps>
afterInput?: React.ComponentType<any>[] afterInput?: CustomComponent[]
beforeInput?: React.ComponentType<any>[] beforeInput?: CustomComponent[]
} }
placeholder?: Record<string, string> | string placeholder?: Record<string, string> | string
rows?: number rows?: number
@@ -310,10 +310,10 @@ export type TextareaField = FieldBase & {
export type CheckboxField = FieldBase & { export type CheckboxField = FieldBase & {
admin?: Admin & { admin?: Admin & {
components?: { components?: {
Error?: React.ComponentType<ErrorProps> Error?: CustomComponent<ErrorProps>
Label?: React.ComponentType<LabelProps> Label?: CustomComponent<LabelProps>
afterInput?: React.ComponentType<any>[] afterInput?: CustomComponent[]
beforeInput?: React.ComponentType<any>[] beforeInput?: CustomComponent[]
} }
} }
type: 'checkbox' type: 'checkbox'
@@ -322,10 +322,10 @@ export type CheckboxField = FieldBase & {
export type DateField = FieldBase & { export type DateField = FieldBase & {
admin?: Admin & { admin?: Admin & {
components?: { components?: {
Error?: React.ComponentType<ErrorProps> Error?: CustomComponent<ErrorProps>
Label?: React.ComponentType<LabelProps> Label?: CustomComponent<LabelProps>
afterInput?: React.ComponentType<any>[] afterInput?: CustomComponent[]
beforeInput?: React.ComponentType<any>[] beforeInput?: CustomComponent[]
} }
date?: ConditionalDateProps date?: ConditionalDateProps
placeholder?: Record<string, string> | string placeholder?: Record<string, string> | string
@@ -416,8 +416,8 @@ export type TabAsField = Tab & {
export type UIField = { export type UIField = {
admin: { admin: {
components?: { components?: {
Cell?: React.ComponentType<any> Cell?: CustomComponent
Field: React.ComponentType<any> Field: CustomComponent
Filter?: React.ComponentType<any> Filter?: React.ComponentType<any>
} }
condition?: Condition condition?: Condition
@@ -435,8 +435,8 @@ export type UIField = {
export type UploadField = FieldBase & { export type UploadField = FieldBase & {
admin?: { admin?: {
components?: { components?: {
Error?: React.ComponentType<ErrorProps> Error?: CustomComponent<ErrorProps>
Label?: React.ComponentType<LabelProps> Label?: CustomComponent<LabelProps>
} }
} }
filterOptions?: FilterOptions filterOptions?: FilterOptions
@@ -447,8 +447,8 @@ export type UploadField = FieldBase & {
type CodeAdmin = Admin & { type CodeAdmin = Admin & {
components?: { components?: {
Error?: React.ComponentType<ErrorProps> Error?: CustomComponent<ErrorProps>
Label?: React.ComponentType<LabelProps> Label?: CustomComponent<LabelProps>
} }
editorOptions?: EditorProps['options'] editorOptions?: EditorProps['options']
language?: string language?: string
@@ -463,8 +463,8 @@ export type CodeField = Omit<FieldBase, 'admin'> & {
type JSONAdmin = Admin & { type JSONAdmin = Admin & {
components?: { components?: {
Error?: React.ComponentType<ErrorProps> Error?: CustomComponent<ErrorProps>
Label?: React.ComponentType<LabelProps> Label?: CustomComponent<LabelProps>
} }
editorOptions?: EditorProps['options'] editorOptions?: EditorProps['options']
} }
@@ -477,8 +477,8 @@ export type JSONField = Omit<FieldBase, 'admin'> & {
export type SelectField = FieldBase & { export type SelectField = FieldBase & {
admin?: Admin & { admin?: Admin & {
components?: { components?: {
Error?: React.ComponentType<ErrorProps> Error?: CustomComponent<ErrorProps>
Label?: React.ComponentType<LabelProps> Label?: CustomComponent<LabelProps>
} }
isClearable?: boolean isClearable?: boolean
isSortable?: boolean isSortable?: boolean
@@ -533,8 +533,8 @@ type SharedRelationshipProperties = FieldBase & {
type RelationshipAdmin = Admin & { type RelationshipAdmin = Admin & {
allowCreate?: boolean allowCreate?: boolean
components?: { components?: {
Error?: React.ComponentType<ErrorProps> Error?: CustomComponent<ErrorProps>
Label?: React.ComponentType<LabelProps> Label?: CustomComponent<LabelProps>
} }
isSortable?: boolean isSortable?: boolean
} }
@@ -574,8 +574,8 @@ export type RichTextField<
> = FieldBase & { > = FieldBase & {
admin?: Admin & { admin?: Admin & {
components?: { components?: {
Error?: React.ComponentType<ErrorProps> Error?: CustomComponent<ErrorProps>
Label?: React.ComponentType<LabelProps> Label?: CustomComponent<LabelProps>
} }
} }
editor?: RichTextAdapter<Value, AdapterProps, AdapterProps> editor?: RichTextAdapter<Value, AdapterProps, AdapterProps>
@@ -618,8 +618,8 @@ export type ArrayField = FieldBase & {
export type RadioField = FieldBase & { export type RadioField = FieldBase & {
admin?: Admin & { admin?: Admin & {
components?: { components?: {
Error?: React.ComponentType<ErrorProps> Error?: CustomComponent<ErrorProps>
Label?: React.ComponentType<LabelProps> Label?: CustomComponent<LabelProps>
} }
layout?: 'horizontal' | 'vertical' layout?: 'horizontal' | 'vertical'
} }

View File

@@ -4,14 +4,23 @@ import { isValidElement } from 'react'
import { isPlainObject } from './isPlainObject.js' import { isPlainObject } from './isPlainObject.js'
export function isReactServerComponent<T extends any>(
component: React.ComponentType | any,
): component is T {
return typeof component === 'function' && isValidElement(component)
}
export function isReactClientComponent<T extends any>(
component: React.ComponentType | any,
): component is T {
// Do this to test for client components (`use client` directive) bc they import as empty objects
return typeof component === 'object' && !isPlainObject(component)
}
export function isReactComponent<T extends any>( export function isReactComponent<T extends any>(
component: React.ComponentType | any, component: React.ComponentType | any,
): component is T { ): component is T {
return ( return isReactServerComponent(component) || isReactClientComponent(component)
(typeof component === 'function' && isValidElement(component)) ||
// Do this to test for client components (`use client` directive) bc they import as empty objects
(typeof component === 'object' && !isPlainObject(component))
)
} }
export function isPlainFunction<T extends Function>(fn: any): fn is T { export function isPlainFunction<T extends Function>(fn: any): fn is T {

View File

@@ -13,7 +13,7 @@ export const getGenerateComponentMap =
(args: { (args: {
resolvedFeatureMap: ResolvedServerFeatureMap resolvedFeatureMap: ResolvedServerFeatureMap
}): RichTextAdapter['generateComponentMap'] => }): RichTextAdapter['generateComponentMap'] =>
({ config, i18n, schemaPath }) => { ({ WithServerSideProps, config, i18n, schemaPath }) => {
const validRelationships = config.collections.map((c) => c.slug) || [] const validRelationships = config.collections.map((c) => c.slug) || []
const componentMap = new Map() const componentMap = new Map()
@@ -50,7 +50,8 @@ export const getGenerateComponentMap =
if (Component) { if (Component) {
componentMap.set( componentMap.set(
`feature.${featureKey}.components.${componentKey}`, `feature.${featureKey}.components.${componentKey}`,
<Component <WithServerSideProps
Component={Component}
componentKey={componentKey} componentKey={componentKey}
featureKey={resolvedFeature.key} featureKey={resolvedFeature.key}
key={`${resolvedFeature.key}-${componentKey}`} key={`${resolvedFeature.key}-${componentKey}`}
@@ -86,6 +87,7 @@ export const getGenerateComponentMap =
}) })
const mappedFields = mapFields({ const mappedFields = mapFields({
WithServerSideProps,
config, config,
disableAddingID: true, disableAddingID: true,
fieldSchema: sanitizedFields, fieldSchema: sanitizedFields,

View File

@@ -14,7 +14,7 @@ import { defaultLeaves as leafTypes } from './field/leaves/index.js'
export const getGenerateComponentMap = export const getGenerateComponentMap =
(args: AdapterArguments): RichTextAdapter['generateComponentMap'] => (args: AdapterArguments): RichTextAdapter['generateComponentMap'] =>
({ config, i18n }) => { ({ WithServerSideProps, config, i18n }) => {
const componentMap = new Map() const componentMap = new Map()
const validRelationships = config.collections.map((c) => c.slug) || [] const validRelationships = config.collections.map((c) => c.slug) || []
@@ -73,6 +73,7 @@ export const getGenerateComponentMap =
}) })
const mappedFields = mapFields({ const mappedFields = mapFields({
WithServerSideProps,
config, config,
fieldSchema: linkFields, fieldSchema: linkFields,
i18n, i18n,
@@ -104,6 +105,7 @@ export const getGenerateComponentMap =
}) })
const mappedFields = mapFields({ const mappedFields = mapFields({
WithServerSideProps,
config, config,
fieldSchema: uploadFields, fieldSchema: uploadFields,
i18n, i18n,

View File

@@ -1,3 +1,4 @@
import { serverProps } from 'payload/config'
import { deepMerge } from 'payload/utilities' import { deepMerge } from 'payload/utilities'
import React from 'react' import React from 'react'
@@ -21,14 +22,23 @@ import React from 'react'
*/ */
export function withMergedProps<ToMergeIntoProps, CompleteReturnProps>({ export function withMergedProps<ToMergeIntoProps, CompleteReturnProps>({
Component, Component,
sanitizeServerOnlyProps = true,
toMergeIntoProps, toMergeIntoProps,
}: { }: {
Component: React.FC<CompleteReturnProps> Component: React.FC<CompleteReturnProps>
sanitizeServerOnlyProps?: boolean
toMergeIntoProps: ToMergeIntoProps toMergeIntoProps: ToMergeIntoProps
}): React.FC<CompleteReturnProps> { }): React.FC<CompleteReturnProps> {
// A wrapper around the args.Component to inject the args.toMergeArgs as props, which are merged with the passed props // A wrapper around the args.Component to inject the args.toMergeArgs as props, which are merged with the passed props
const MergedPropsComponent: React.FC<CompleteReturnProps> = (passedProps) => { const MergedPropsComponent: React.FC<CompleteReturnProps> = (passedProps) => {
const mergedProps = deepMerge(passedProps, toMergeIntoProps) const mergedProps = deepMerge(passedProps, toMergeIntoProps)
if (sanitizeServerOnlyProps) {
serverProps.forEach((prop) => {
delete (mergedProps)[prop]
})
}
return <Component {...mergedProps} /> return <Component {...mergedProps} />
} }

View File

@@ -10,7 +10,7 @@ import { withCondition } from '../../forms/withCondition/index.js'
export type HiddenInputFieldProps = FormFieldBase & { export type HiddenInputFieldProps = FormFieldBase & {
disableModifyingForm?: false disableModifyingForm?: false
forceUsePathFromProps?: boolean forceUsePathFromProps?: boolean
name: string name?: string
path?: string path?: string
value?: unknown value?: unknown
} }

View File

@@ -0,0 +1,21 @@
import type { WithServerSideProps as WithServerSidePropsType } from 'payload/types'
import { isReactServerComponent } from 'payload/utilities'
import React from 'react'
export const WithServerSideProps: WithServerSidePropsType = ({ Component, payload, ...rest }) => {
if (Component) {
const WithServerSideProps: React.FC<any> = (passedProps) => {
const propsWithPayload = {
...passedProps,
...(isReactServerComponent(Component) ? { payload } : {}),
}
return <Component {...propsWithPayload} />
}
return <WithServerSideProps {...rest} />
}
return null
}

View File

@@ -1,14 +1,19 @@
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload/types' import type {
SanitizedCollectionConfig,
SanitizedGlobalConfig,
WithServerSideProps as WithServerSidePropsType,
} from 'payload/types'
import React from 'react' import React from 'react'
import type { ActionMap } from './types.js' import type { ActionMap } from './types.js'
export const mapActions = (args: { export const mapActions = (args: {
WithServerSideProps: WithServerSidePropsType
collectionConfig?: SanitizedCollectionConfig collectionConfig?: SanitizedCollectionConfig
globalConfig?: SanitizedGlobalConfig globalConfig?: SanitizedGlobalConfig
}): ActionMap => { }): ActionMap => {
const { collectionConfig, globalConfig } = args const { WithServerSideProps, collectionConfig, globalConfig } = args
const editViews = (collectionConfig || globalConfig)?.admin?.components?.views?.Edit const editViews = (collectionConfig || globalConfig)?.admin?.components?.views?.Edit
@@ -28,7 +33,7 @@ export const mapActions = (args: {
view.actions.forEach((action) => { view.actions.forEach((action) => {
const Action = action const Action = action
if (typeof Action === 'function') { if (typeof Action === 'function') {
result.Edit[key] = [...(result[key] || []), <Action />] result.Edit[key] = [...(result[key] || []), <WithServerSideProps Component={Action} />]
} }
}) })
} }
@@ -40,7 +45,7 @@ export const mapActions = (args: {
const Action = action const Action = action
if (typeof Action === 'function') { if (typeof Action === 'function') {
// eslint-disable-next-line @typescript-eslint/no-floating-promises // eslint-disable-next-line @typescript-eslint/no-floating-promises
result.List = [...result.List, <Action />] result.List = [...result.List, <WithServerSideProps Component={Action} />]
} }
}) })
} }

View File

@@ -7,6 +7,7 @@ import type {
EntityDescriptionFunction, EntityDescriptionFunction,
SanitizedCollectionConfig, SanitizedCollectionConfig,
SanitizedConfig, SanitizedConfig,
WithServerSideProps as WithServerSidePropsType,
} from 'payload/types' } from 'payload/types'
import { ViewDescription } from '@payloadcms/ui/elements/ViewDescription' import { ViewDescription } from '@payloadcms/ui/elements/ViewDescription'
@@ -21,6 +22,7 @@ import { mapFields } from './fields.js'
export const mapCollections = ({ export const mapCollections = ({
DefaultEditView, DefaultEditView,
DefaultListView, DefaultListView,
WithServerSideProps,
collections, collections,
config, config,
i18n, i18n,
@@ -28,6 +30,7 @@ export const mapCollections = ({
}: { }: {
DefaultEditView: React.FC<EditViewProps> DefaultEditView: React.FC<EditViewProps>
DefaultListView: React.FC<AdminViewProps> DefaultListView: React.FC<AdminViewProps>
WithServerSideProps: WithServerSidePropsType
collections: SanitizedCollectionConfig[] collections: SanitizedCollectionConfig[]
config: SanitizedConfig config: SanitizedConfig
i18n: I18n i18n: I18n
@@ -71,44 +74,60 @@ export const mapCollections = ({
const List = CustomListView || DefaultListView const List = CustomListView || DefaultListView
const beforeList = collectionConfig?.admin?.components?.BeforeList
const beforeListTable = collectionConfig?.admin?.components?.BeforeListTable
const afterList = collectionConfig?.admin?.components?.AfterList
const afterListTable = collectionConfig?.admin?.components?.AfterListTable
const SaveButtonComponent = collectionConfig?.admin?.components?.edit?.SaveButton const SaveButtonComponent = collectionConfig?.admin?.components?.edit?.SaveButton
const SaveButton = SaveButtonComponent ? <SaveButtonComponent /> : undefined
const SaveButton = SaveButtonComponent ? (
<WithServerSideProps Component={SaveButtonComponent} />
) : undefined
const SaveDraftButtonComponent = collectionConfig?.admin?.components?.edit?.SaveDraftButton const SaveDraftButtonComponent = collectionConfig?.admin?.components?.edit?.SaveDraftButton
const SaveDraftButton = SaveDraftButtonComponent ? <SaveDraftButtonComponent /> : undefined
const SaveDraftButton = SaveDraftButtonComponent ? (
<WithServerSideProps Component={SaveDraftButtonComponent} />
) : undefined
const PreviewButtonComponent = collectionConfig?.admin?.components?.edit?.PreviewButton const PreviewButtonComponent = collectionConfig?.admin?.components?.edit?.PreviewButton
const PreviewButton = PreviewButtonComponent ? <PreviewButtonComponent /> : undefined
const PreviewButton = PreviewButtonComponent ? (
<WithServerSideProps Component={PreviewButtonComponent} />
) : undefined
const PublishButtonComponent = collectionConfig?.admin?.components?.edit?.PublishButton const PublishButtonComponent = collectionConfig?.admin?.components?.edit?.PublishButton
const PublishButton = PublishButtonComponent ? <PublishButtonComponent /> : undefined
const PublishButton = PublishButtonComponent ? (
<WithServerSideProps Component={PublishButtonComponent} />
) : undefined
const beforeList = collectionConfig?.admin?.components?.BeforeList
const BeforeList = const BeforeList =
(beforeList && Array.isArray(beforeList) && beforeList?.map((Component) => <Component />)) || (beforeList &&
Array.isArray(beforeList) &&
beforeList?.map((Component) => <WithServerSideProps Component={Component} />)) ||
null null
const beforeListTable = collectionConfig?.admin?.components?.BeforeListTable
const BeforeListTable = const BeforeListTable =
(beforeListTable && (beforeListTable &&
Array.isArray(beforeListTable) && Array.isArray(beforeListTable) &&
beforeListTable?.map((Component) => <Component />)) || beforeListTable?.map((Component) => <WithServerSideProps Component={Component} />)) ||
null null
const afterList = collectionConfig?.admin?.components?.AfterList
const AfterList = const AfterList =
(afterList && Array.isArray(afterList) && afterList?.map((Component) => <Component />)) || (afterList &&
Array.isArray(afterList) &&
afterList?.map((Component) => <WithServerSideProps Component={Component} />)) ||
null null
const afterListTable = collectionConfig?.admin?.components?.AfterListTable
const AfterListTable = const AfterListTable =
(afterListTable && (afterListTable &&
Array.isArray(afterListTable) && Array.isArray(afterListTable) &&
afterListTable?.map((Component) => <Component />)) || afterListTable?.map((Component) => <WithServerSideProps Component={Component} />)) ||
null null
const descriptionProps: ViewDescriptionProps = { const descriptionProps: ViewDescriptionProps = {
@@ -134,7 +153,7 @@ export const mapCollections = ({
const Description = const Description =
DescriptionComponent !== undefined ? ( DescriptionComponent !== undefined ? (
<DescriptionComponent {...(descriptionProps || {})} /> <WithServerSideProps Component={DescriptionComponent} {...(descriptionProps || {})} />
) : undefined ) : undefined
const componentMap: CollectionComponentMap = { const componentMap: CollectionComponentMap = {
@@ -150,9 +169,11 @@ export const mapCollections = ({
SaveButton, SaveButton,
SaveDraftButton, SaveDraftButton,
actionsMap: mapActions({ actionsMap: mapActions({
WithServerSideProps,
collectionConfig, collectionConfig,
}), }),
fieldMap: mapFields({ fieldMap: mapFields({
WithServerSideProps,
config, config,
fieldSchema: fields, fieldSchema: fields,
i18n, i18n,

View File

@@ -1,4 +1,5 @@
import type { I18n } from '@payloadcms/translations' import type { I18n } from '@payloadcms/translations'
import type { CustomComponent } from 'packages/payload/src/config/types.js'
import type { import type {
CellComponentProps, CellComponentProps,
DescriptionComponent, DescriptionComponent,
@@ -11,11 +12,17 @@ import type {
Option, Option,
RowLabelComponent, RowLabelComponent,
SanitizedConfig, SanitizedConfig,
WithServerSideProps as WithServerSidePropsType,
} from 'payload/types' } from 'payload/types'
import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription' import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription'
import { fieldAffectsData, fieldIsPresentationalOnly } from 'payload/types' import { fieldAffectsData, fieldIsPresentationalOnly } from 'payload/types'
import { isPlainFunction, isReactComponent } from 'payload/utilities' import {
isPlainFunction,
isReactClientComponent,
isReactComponent,
isReactServerComponent,
} from 'payload/utilities'
import React, { Fragment } from 'react' import React, { Fragment } from 'react'
import type { ArrayFieldProps } from '../../../fields/Array/index.js' import type { ArrayFieldProps } from '../../../fields/Array/index.js'
@@ -50,6 +57,7 @@ import type {
import { HiddenInput } from '../../../fields/HiddenInput/index.js' import { HiddenInput } from '../../../fields/HiddenInput/index.js'
export const mapFields = (args: { export const mapFields = (args: {
WithServerSideProps: WithServerSidePropsType
config: SanitizedConfig config: SanitizedConfig
/** /**
* If mapFields is used outside of collections, you might not want it to add an id field * If mapFields is used outside of collections, you might not want it to add an id field
@@ -62,6 +70,7 @@ export const mapFields = (args: {
readOnly?: boolean readOnly?: boolean
}): FieldMap => { }): FieldMap => {
const { const {
WithServerSideProps,
config, config,
disableAddingID, disableAddingID,
fieldSchema, fieldSchema,
@@ -74,8 +83,7 @@ export const mapFields = (args: {
const result: FieldMap = fieldSchema.reduce((acc, field): FieldMap => { const result: FieldMap = fieldSchema.reduce((acc, field): FieldMap => {
const fieldIsPresentational = fieldIsPresentationalOnly(field) const fieldIsPresentational = fieldIsPresentationalOnly(field)
let CustomFieldComponent: React.ComponentType<FieldComponentProps> = let CustomFieldComponent: CustomComponent<FieldComponentProps> = field.admin?.components?.Field
field.admin?.components?.Field
const CustomCellComponent = field.admin?.components?.Cell const CustomCellComponent = field.admin?.components?.Cell
@@ -102,7 +110,7 @@ export const mapFields = (args: {
Array.isArray(field.admin?.components?.afterInput) && ( Array.isArray(field.admin?.components?.afterInput) && (
<Fragment> <Fragment>
{field.admin.components.afterInput.map((Component, i) => ( {field.admin.components.afterInput.map((Component, i) => (
<Component key={i} /> <WithServerSideProps Component={Component} key={i} />
))} ))}
</Fragment> </Fragment>
)) || )) ||
@@ -115,7 +123,7 @@ export const mapFields = (args: {
Array.isArray(field.admin.components.beforeInput) && ( Array.isArray(field.admin.components.beforeInput) && (
<Fragment> <Fragment>
{field.admin.components.beforeInput.map((Component, i) => ( {field.admin.components.beforeInput.map((Component, i) => (
<Component key={i} /> <WithServerSideProps Component={Component} key={i} />
))} ))}
</Fragment> </Fragment>
)) || )) ||
@@ -133,12 +141,12 @@ export const mapFields = (args: {
('admin' in field && ('admin' in field &&
field.admin?.components && field.admin?.components &&
'Label' in field.admin.components && 'Label' in field.admin.components &&
field.admin?.components?.Label) || field.admin.components?.Label) ||
undefined undefined
const CustomLabel = const CustomLabel =
CustomLabelComponent !== undefined ? ( CustomLabelComponent !== undefined ? (
<CustomLabelComponent {...(labelProps || {})} /> <WithServerSideProps Component={CustomLabelComponent} {...(labelProps || {})} />
) : undefined ) : undefined
const descriptionProps: FieldDescriptionProps = { const descriptionProps: FieldDescriptionProps = {
@@ -164,7 +172,10 @@ export const mapFields = (args: {
const CustomDescription = const CustomDescription =
CustomDescriptionComponent !== undefined ? ( CustomDescriptionComponent !== undefined ? (
<CustomDescriptionComponent {...(descriptionProps || {})} /> <WithServerSideProps
Component={CustomDescriptionComponent}
{...(descriptionProps || {})}
/>
) : undefined ) : undefined
const errorProps = { const errorProps = {
@@ -180,7 +191,7 @@ export const mapFields = (args: {
const CustomError = const CustomError =
CustomErrorComponent !== undefined ? ( CustomErrorComponent !== undefined ? (
<CustomErrorComponent {...(errorProps || {})} /> <WithServerSideProps Component={CustomErrorComponent} {...(errorProps || {})} />
) : undefined ) : undefined
const baseFieldProps: FormFieldBase = { const baseFieldProps: FormFieldBase = {
@@ -239,7 +250,7 @@ export const mapFields = (args: {
isReactComponent<RowLabelComponent>(field.admin.components.RowLabel) isReactComponent<RowLabelComponent>(field.admin.components.RowLabel)
) { ) {
const CustomRowLabelComponent = field.admin.components.RowLabel const CustomRowLabelComponent = field.admin.components.RowLabel
CustomRowLabel = <CustomRowLabelComponent /> CustomRowLabel = <WithServerSideProps Component={CustomRowLabelComponent} />
} }
const arrayFieldProps: Omit<ArrayFieldProps, 'indexPath' | 'permissions'> = { const arrayFieldProps: Omit<ArrayFieldProps, 'indexPath' | 'permissions'> = {
@@ -249,6 +260,7 @@ export const mapFields = (args: {
className: field.admin?.className, className: field.admin?.className,
disabled: field.admin?.disabled, disabled: field.admin?.disabled,
fieldMap: mapFields({ fieldMap: mapFields({
WithServerSideProps,
config, config,
fieldSchema: field.fields, fieldSchema: field.fields,
filter, filter,
@@ -272,6 +284,7 @@ export const mapFields = (args: {
case 'blocks': { case 'blocks': {
const blocks = field.blocks.map((block) => { const blocks = field.blocks.map((block) => {
const blockFieldMap = mapFields({ const blockFieldMap = mapFields({
WithServerSideProps,
config, config,
fieldSchema: block.fields, fieldSchema: block.fields,
filter, filter,
@@ -355,7 +368,9 @@ export const mapFields = (args: {
if (isReactComponent(field.label) || isPlainFunction(field.label)) { if (isReactComponent(field.label) || isPlainFunction(field.label)) {
const CustomCollapsibleLabelComponent = field.label as RowLabelComponent const CustomCollapsibleLabelComponent = field.label as RowLabelComponent
CustomCollapsibleLabel = <CustomCollapsibleLabelComponent /> CustomCollapsibleLabel = (
<WithServerSideProps Component={CustomCollapsibleLabelComponent} />
)
} }
const collapsibleField: Omit<CollapsibleFieldProps, 'indexPath' | 'permissions'> = { const collapsibleField: Omit<CollapsibleFieldProps, 'indexPath' | 'permissions'> = {
@@ -364,6 +379,7 @@ export const mapFields = (args: {
className: field.admin?.className, className: field.admin?.className,
disabled: field.admin?.disabled, disabled: field.admin?.disabled,
fieldMap: mapFields({ fieldMap: mapFields({
WithServerSideProps,
config, config,
disableAddingID: true, disableAddingID: true,
fieldSchema: field.fields, fieldSchema: field.fields,
@@ -426,6 +442,7 @@ export const mapFields = (args: {
className: field.admin?.className, className: field.admin?.className,
disabled: field.admin?.disabled, disabled: field.admin?.disabled,
fieldMap: mapFields({ fieldMap: mapFields({
WithServerSideProps,
config, config,
disableAddingID: true, disableAddingID: true,
fieldSchema: field.fields, fieldSchema: field.fields,
@@ -553,7 +570,12 @@ export const mapFields = (args: {
const RichTextCellComponent = field.editor.CellComponent const RichTextCellComponent = field.editor.CellComponent
if (typeof field.editor.generateComponentMap === 'function') { if (typeof field.editor.generateComponentMap === 'function') {
const result = field.editor.generateComponentMap({ config, i18n, schemaPath: path }) const result = field.editor.generateComponentMap({
WithServerSideProps,
config,
i18n,
schemaPath: path,
})
richTextField.richTextComponentMap = result richTextField.richTextComponentMap = result
cellComponentProps.richTextComponentMap = result cellComponentProps.richTextComponentMap = result
} }
@@ -563,7 +585,9 @@ export const mapFields = (args: {
} }
if (RichTextCellComponent) { if (RichTextCellComponent) {
cellComponentProps.CellComponentOverride = <RichTextCellComponent /> cellComponentProps.CellComponentOverride = (
<WithServerSideProps Component={RichTextCellComponent} />
)
} }
fieldComponentProps = richTextField fieldComponentProps = richTextField
@@ -576,6 +600,7 @@ export const mapFields = (args: {
className: field.admin?.className, className: field.admin?.className,
disabled: field.admin?.disabled, disabled: field.admin?.disabled,
fieldMap: mapFields({ fieldMap: mapFields({
WithServerSideProps,
config, config,
disableAddingID: true, disableAddingID: true,
fieldSchema: field.fields, fieldSchema: field.fields,
@@ -597,6 +622,7 @@ export const mapFields = (args: {
// `tabs` fields require a field map of each of its tab's nested fields // `tabs` fields require a field map of each of its tab's nested fields
const tabs = field.tabs.map((tab) => { const tabs = field.tabs.map((tab) => {
const tabFieldMap = mapFields({ const tabFieldMap = mapFields({
WithServerSideProps,
config, config,
disableAddingID: true, disableAddingID: true,
fieldSchema: tab.fields, fieldSchema: tab.fields,
@@ -722,10 +748,10 @@ export const mapFields = (args: {
name: 'name' in field ? field.name : undefined, name: 'name' in field ? field.name : undefined,
type: field.type, type: field.type,
CustomCell: CustomCellComponent ? ( CustomCell: CustomCellComponent ? (
<CustomCellComponent {...cellComponentProps} /> <WithServerSideProps Component={CustomCellComponent} {...cellComponentProps} />
) : undefined, ) : undefined,
CustomField: CustomFieldComponent ? ( CustomField: CustomFieldComponent ? (
<CustomFieldComponent {...fieldComponentProps} /> <WithServerSideProps Component={CustomFieldComponent} {...fieldComponentProps} />
) : undefined, ) : undefined,
cellComponentProps, cellComponentProps,
disableBulkEdit: disableBulkEdit:

View File

@@ -5,6 +5,7 @@ import type {
EntityDescriptionFunction, EntityDescriptionFunction,
SanitizedConfig, SanitizedConfig,
SanitizedGlobalConfig, SanitizedGlobalConfig,
WithServerSideProps as WithServerSidePropsType,
} from 'payload/types' } from 'payload/types'
import { ViewDescription, type ViewDescriptionProps } from '@payloadcms/ui/elements/ViewDescription' import { ViewDescription, type ViewDescriptionProps } from '@payloadcms/ui/elements/ViewDescription'
@@ -18,12 +19,14 @@ import { mapFields } from './fields.js'
export const mapGlobals = ({ export const mapGlobals = ({
DefaultEditView, DefaultEditView,
WithServerSideProps,
config, config,
globals, globals,
i18n, i18n,
readOnly: readOnlyOverride, readOnly: readOnlyOverride,
}: { }: {
DefaultEditView: React.FC<EditViewProps> DefaultEditView: React.FC<EditViewProps>
WithServerSideProps: WithServerSidePropsType
config: SanitizedConfig config: SanitizedConfig
globals: SanitizedGlobalConfig[] globals: SanitizedGlobalConfig[]
i18n: I18n i18n: I18n
@@ -37,16 +40,28 @@ export const mapGlobals = ({
const editViewFromConfig = globalConfig?.admin?.components?.views?.Edit const editViewFromConfig = globalConfig?.admin?.components?.views?.Edit
const SaveButton = globalConfig?.admin?.components?.elements?.SaveButton const SaveButton = globalConfig?.admin?.components?.elements?.SaveButton
const SaveButtonComponent = SaveButton ? <SaveButton /> : undefined
const SaveButtonComponent = SaveButton ? (
<WithServerSideProps Component={SaveButton} />
) : undefined
const SaveDraftButton = globalConfig?.admin?.components?.elements?.SaveDraftButton const SaveDraftButton = globalConfig?.admin?.components?.elements?.SaveDraftButton
const SaveDraftButtonComponent = SaveDraftButton ? <SaveDraftButton /> : undefined
const SaveDraftButtonComponent = SaveDraftButton ? (
<WithServerSideProps Component={SaveDraftButton} />
) : undefined
const PreviewButton = globalConfig?.admin?.components?.elements?.PreviewButton const PreviewButton = globalConfig?.admin?.components?.elements?.PreviewButton
const PreviewButtonComponent = PreviewButton ? <PreviewButton /> : undefined
const PreviewButtonComponent = PreviewButton ? (
<WithServerSideProps Component={PreviewButton} />
) : undefined
const PublishButton = globalConfig?.admin?.components?.elements?.PublishButton const PublishButton = globalConfig?.admin?.components?.elements?.PublishButton
const PublishButtonComponent = PublishButton ? <PublishButton /> : undefined
const PublishButtonComponent = PublishButton ? (
<WithServerSideProps Component={PublishButton} />
) : undefined
const CustomEditView = const CustomEditView =
typeof editViewFromConfig === 'function' typeof editViewFromConfig === 'function'
@@ -84,7 +99,7 @@ export const mapGlobals = ({
const Description = const Description =
DescriptionComponent !== undefined ? ( DescriptionComponent !== undefined ? (
<DescriptionComponent {...(descriptionProps || {})} /> <WithServerSideProps Component={DescriptionComponent} {...(descriptionProps || {})} />
) : undefined ) : undefined
const componentMap: GlobalComponentMap = { const componentMap: GlobalComponentMap = {
@@ -95,9 +110,11 @@ export const mapGlobals = ({
SaveButton: SaveButtonComponent, SaveButton: SaveButtonComponent,
SaveDraftButton: SaveDraftButtonComponent, SaveDraftButton: SaveDraftButtonComponent,
actionsMap: mapActions({ actionsMap: mapActions({
WithServerSideProps,
globalConfig, globalConfig,
}), }),
fieldMap: mapFields({ fieldMap: mapFields({
WithServerSideProps,
config, config,
fieldSchema: fields, fieldSchema: fields,
i18n, i18n,

View File

@@ -1,10 +1,17 @@
import type { I18n } from '@payloadcms/translations' import type { I18n } from '@payloadcms/translations'
import type { AdminViewProps, EditViewProps, SanitizedConfig } from 'payload/types' import type {
AdminViewProps,
EditViewProps,
Payload,
SanitizedConfig,
WithServerSideProps as WithServerSidePropsType,
} from 'payload/types'
import React from 'react' import React from 'react'
import type { ComponentMap } from './types.js' import type { ComponentMap } from './types.js'
import { WithServerSideProps as WithServerSidePropsGeneric } from './WithServerSideProps.js'
import { mapCollections } from './collections.js' import { mapCollections } from './collections.js'
import { mapGlobals } from './globals.js' import { mapGlobals } from './globals.js'
@@ -14,16 +21,22 @@ export const buildComponentMap = (args: {
children: React.ReactNode children: React.ReactNode
config: SanitizedConfig config: SanitizedConfig
i18n: I18n i18n: I18n
payload: Payload
readOnly?: boolean readOnly?: boolean
}): { }): {
componentMap: ComponentMap componentMap: ComponentMap
wrappedChildren: React.ReactNode wrappedChildren: React.ReactNode
} => { } => {
const { DefaultEditView, DefaultListView, children, config, i18n, readOnly } = args const { DefaultEditView, DefaultListView, children, config, i18n, payload, readOnly } = args
const WithServerSideProps: WithServerSidePropsType = ({ Component, ...rest }) => {
return <WithServerSidePropsGeneric Component={Component} payload={payload} {...rest} />
}
const collections = mapCollections({ const collections = mapCollections({
DefaultEditView, DefaultEditView,
DefaultListView, DefaultListView,
WithServerSideProps,
collections: config.collections, collections: config.collections,
config, config,
i18n, i18n,
@@ -32,6 +45,7 @@ export const buildComponentMap = (args: {
const globals = mapGlobals({ const globals = mapGlobals({
DefaultEditView, DefaultEditView,
WithServerSideProps,
config, config,
globals: config.globals, globals: config.globals,
i18n, i18n,
@@ -52,17 +66,21 @@ export const buildComponentMap = (args: {
const LogoutButtonComponent = config.admin?.components?.logout?.Button const LogoutButtonComponent = config.admin?.components?.logout?.Button
const LogoutButton = LogoutButtonComponent ? <LogoutButtonComponent /> : null const LogoutButton = LogoutButtonComponent ? (
<WithServerSideProps Component={LogoutButtonComponent} />
) : null
const IconComponent = config.admin?.components?.graphics?.Icon const IconComponent = config.admin?.components?.graphics?.Icon
const Icon = IconComponent ? <IconComponent /> : null const Icon = IconComponent ? <WithServerSideProps Component={IconComponent} /> : null
return { return {
componentMap: { componentMap: {
Icon, Icon,
LogoutButton, LogoutButton,
actions: config.admin?.components?.actions?.map((Component) => <Component />), actions: config.admin?.components?.actions?.map((Component) => (
<WithServerSideProps Component={Component} />
)),
collections, collections,
globals, globals,
}, },

View File

@@ -17,6 +17,8 @@ export type IComponentMapContext = {
}) => MappedField | undefined }) => MappedField | undefined
} }
export { WithServerSideProps } from './buildComponentMap/WithServerSideProps.js'
const ComponentMapContext = createContext<IComponentMapContext>({} as IComponentMapContext) const ComponentMapContext = createContext<IComponentMapContext>({} as IComponentMapContext)
export const ComponentMapProvider: React.FC<{ export const ComponentMapProvider: React.FC<{

View File

@@ -1,8 +1,10 @@
import type { SanitizedConfig } from 'payload/types'
import React from 'react' import React from 'react'
const baseClass = 'admin-button' const baseClass = 'admin-button'
export const AdminButton: React.FC = () => { export const AdminButton: SanitizedConfig['admin']['components']['actions'][0] = () => {
return ( return (
<div <div
className={baseClass} className={baseClass}

View File

@@ -1,10 +1,12 @@
import type { SanitizedConfig } from 'payload/types'
import React from 'react' import React from 'react'
import './index.scss' import './index.scss'
const baseClass = 'after-dashboard' const baseClass = 'after-dashboard'
export const AfterDashboard: React.FC = () => { export const AfterDashboard: SanitizedConfig['admin']['components']['afterDashboard'][0] = () => {
return ( return (
<div className={baseClass}> <div className={baseClass}>
<h4>Test Config</h4> <h4>Test Config</h4>

View File

@@ -1,5 +1,7 @@
'use client' 'use client'
import type { SanitizedConfig } from 'payload/types'
import { useConfig } from '@payloadcms/ui/providers/Config' import { useConfig } from '@payloadcms/ui/providers/Config'
import LinkImport from 'next/link.js' import LinkImport from 'next/link.js'
const Link = (LinkImport.default || LinkImport) as unknown as typeof LinkImport.default const Link = (LinkImport.default || LinkImport) as unknown as typeof LinkImport.default
@@ -8,7 +10,7 @@ import React from 'react'
const baseClass = 'after-nav-links' const baseClass = 'after-nav-links'
export const AfterNavLinks: React.FC = () => { export const AfterNavLinks: SanitizedConfig['admin']['components']['afterNavLinks'][0] = () => {
const { const {
routes: { admin: adminRoute }, routes: { admin: adminRoute },
} = useConfig() } = useConfig()

View File

@@ -1,10 +1,13 @@
'use client' 'use client'
import type { SanitizedConfig } from 'payload/types'
import { useTranslation } from '@payloadcms/ui/providers/Translation' import { useTranslation } from '@payloadcms/ui/providers/Translation'
import React from 'react' import React from 'react'
export const BeforeLogin: React.FC = () => { export const BeforeLogin: SanitizedConfig['admin']['components']['beforeLogin'][0] = () => {
const translation = useTranslation() const translation = useTranslation()
return ( return (
<div> <div>
<h3>{translation.t('general:welcome')}</h3> <h3>{translation.t('general:welcome')}</h3>

View File

@@ -1,8 +1,10 @@
import type { CustomComponent } from 'payload/config'
import React from 'react' import React from 'react'
const baseClass = 'collection-api-button' const baseClass = 'collection-api-button'
export const CollectionAPIButton: React.FC = () => { export const CollectionAPIButton: CustomComponent = () => {
return ( return (
<div <div
className={baseClass} className={baseClass}

View File

@@ -1,8 +1,10 @@
import type { CustomComponent } from 'payload/config'
import React from 'react' import React from 'react'
const baseClass = 'collection-edit-button' const baseClass = 'collection-edit-button'
export const CollectionEditButton: React.FC = () => { export const CollectionEditButton: CustomComponent = () => {
return ( return (
<div <div
className={baseClass} className={baseClass}

View File

@@ -1,8 +1,10 @@
import type { CustomComponent } from 'payload/config'
import React from 'react' import React from 'react'
const baseClass = 'collection-list-button' const baseClass = 'collection-list-button'
export const CollectionListButton: React.FC = () => { export const CollectionListButton: CustomComponent = () => {
return ( return (
<div <div
className={baseClass} className={baseClass}

View File

@@ -161,4 +161,4 @@
".next/types/**/*.ts", ".next/types/**/*.ts",
"scripts/**/*.ts" "scripts/**/*.ts"
] ]
} }