fix!: some custom components were not handled properly if they are RSCs (#6315)

**Breaking:** The following, exported components now need the `payload` object as a prop rather than the `config` object:
- `RenderCustomComponent` (optional)
- `Logo`
- `DefaultTemplate`
- `DefaultNav`
This commit is contained in:
Alessio Gravili
2024-05-13 12:05:13 -04:00
committed by GitHub
parent 23c7ab2bc4
commit 0d98b4b96f
16 changed files with 94 additions and 38 deletions

View File

@@ -77,6 +77,7 @@ export const Account: React.FC<AdminViewProps> = ({ initPageResult, params, sear
}
DefaultComponent={EditView}
componentProps={viewComponentProps}
payload={payload}
/>
</FormQueryParamsProvider>
</DocumentInfoProvider>

View File

@@ -1,8 +1,9 @@
import type { Permissions } from 'payload/auth'
import type { SanitizedConfig, VisibleEntities } from 'payload/types'
import type { Payload, SanitizedConfig, VisibleEntities } from 'payload/types'
import { Gutter } from '@payloadcms/ui/elements/Gutter'
import { SetStepNav } from '@payloadcms/ui/elements/StepNav'
import { WithServerSideProps } from '@payloadcms/ui/elements/WithServerSideProps'
import { SetViewActions } from '@payloadcms/ui/providers/Actions'
import React from 'react'
@@ -14,6 +15,7 @@ const baseClass = 'dashboard'
export type DashboardProps = {
Link: React.ComponentType<any>
config: SanitizedConfig
payload: Payload
permissions: Permissions
visibleEntities: VisibleEntities
}
@@ -26,24 +28,36 @@ export const DefaultDashboard: React.FC<DashboardProps> = (props) => {
components: { afterDashboard, beforeDashboard },
},
},
payload,
permissions,
visibleEntities,
} = props
const BeforeDashboards = Array.isArray(beforeDashboard)
? beforeDashboard.map((Component, i) => (
<WithServerSideProps Component={Component} key={i} payload={payload} />
))
: null
const AfterDashboards = Array.isArray(afterDashboard)
? afterDashboard.map((Component, i) => (
<WithServerSideProps Component={Component} key={i} payload={payload} />
))
: null
return (
<div className={baseClass}>
<SetStepNav nav={[]} />
<SetViewActions actions={[]} />
<Gutter className={`${baseClass}__wrap`}>
{Array.isArray(beforeDashboard) &&
beforeDashboard.map((Component, i) => <Component key={i} />)}
{Array.isArray(BeforeDashboards) && BeforeDashboards.map((Component) => Component)}
<DefaultDashboardClient
Link={Link}
permissions={permissions}
visibleEntities={visibleEntities}
/>
{Array.isArray(afterDashboard) &&
afterDashboard.map((Component, i) => <Component key={i} />)}
{Array.isArray(AfterDashboards) && AfterDashboards.map((Component) => Component)}
</Gutter>
</div>
)

View File

@@ -18,6 +18,7 @@ export const Dashboard: React.FC<AdminViewProps> = ({ initPageResult }) => {
permissions,
req: {
payload: { config },
payload,
user,
},
visibleEntities,
@@ -25,7 +26,7 @@ export const Dashboard: React.FC<AdminViewProps> = ({ initPageResult }) => {
const CustomDashboardComponent = config.admin.components?.views?.Dashboard
const viewComponentProps: DashboardProps = {
const viewComponentProps: Omit<DashboardProps, 'payload'> = {
Link,
config,
permissions,
@@ -41,6 +42,7 @@ export const Dashboard: React.FC<AdminViewProps> = ({ initPageResult }) => {
}
DefaultComponent={DefaultDashboard}
componentProps={viewComponentProps}
payload={payload}
/>
</Fragment>
)

View File

@@ -220,6 +220,7 @@ export const Document: React.FC<AdminViewProps> = async ({
CustomComponent={ViewOverride || CustomView}
DefaultComponent={DefaultView}
componentProps={viewComponentProps}
payload={payload}
/>
)}
</FormQueryParamsProvider>

View File

@@ -142,6 +142,7 @@ export const ListView: React.FC<AdminViewProps> = async ({ initPageResult, searc
CustomComponent={CustomListView}
DefaultComponent={DefaultListView}
componentProps={viewComponentProps}
payload={payload}
/>
</TableColumnsProvider>
</ListQueryProvider>

View File

@@ -48,7 +48,7 @@ export const LoginView: React.FC<AdminViewProps> = ({ initPageResult, searchPara
return (
<Fragment>
<div className={`${loginBaseClass}__brand`}>
<Logo config={config} />
<Logo payload={payload} />
</div>
{Array.isArray(BeforeLogins) && BeforeLogins.map((Component) => Component)}
{!collectionConfig?.auth?.disableLocalStrategy && <LoginForm searchParams={searchParams} />}

View File

@@ -60,7 +60,7 @@ export const NotFoundPage = async ({
<Fragment>
<HydrateClientUser permissions={initPageResult.permissions} user={initPageResult.req.user} />
<DefaultTemplate
config={initPageResult.req.payload.config}
payload={initPageResult.req.payload}
visibleEntities={initPageResult.visibleEntities}
>
<NotFoundClient />

View File

@@ -87,7 +87,10 @@ export const RootPage = async ({
<MinimalTemplate className={templateClassName}>{RenderedView}</MinimalTemplate>
)}
{templateType === 'default' && (
<DefaultTemplate config={config} visibleEntities={initPageResult.visibleEntities}>
<DefaultTemplate
payload={initPageResult?.req.payload}
visibleEntities={initPageResult.visibleEntities}
>
{RenderedView}
</DefaultTemplate>
)}

View File

@@ -18,6 +18,7 @@ export const Verify: React.FC<AdminViewProps> = async ({ initPageResult, params
const {
payload: { config },
payload,
} = req
const {
@@ -42,7 +43,7 @@ export const Verify: React.FC<AdminViewProps> = async ({ initPageResult, params
return (
<React.Fragment>
<div className={`${verifyBaseClass}__brand`}>
<Logo config={config} />
<Logo payload={payload} />
</div>
<h2>{textToRender}</h2>
</React.Fragment>

View File

@@ -121,6 +121,9 @@ type Admin = {
Cell?: CustomComponent
Description?: DescriptionComponent
Field?: CustomComponent
/**
* The Filter component has to be a client component
*/
Filter?: React.ComponentType<any>
}
/**
@@ -447,6 +450,9 @@ export type UIField = {
components?: {
Cell?: CustomComponent
Field: CustomComponent
/**
* The Filter component has to be a client component
*/
Filter?: React.ComponentType<any>
}
condition?: Condition

View File

@@ -1,4 +1,4 @@
import type { SanitizedConfig } from 'payload/types'
import type { Payload } from 'payload/types'
import React from 'react'
@@ -9,16 +9,18 @@ import './index.scss'
const baseClass = 'nav'
import { WithServerSideProps } from '@payloadcms/ui/elements/WithServerSideProps'
import { DefaultNavClient } from './index.client.js'
export type NavProps = {
config: SanitizedConfig
payload: Payload
}
export const DefaultNav: React.FC<NavProps> = (props) => {
const { config } = props
const { payload } = props
if (!config) {
if (!payload?.config) {
return null
}
@@ -26,15 +28,28 @@ export const DefaultNav: React.FC<NavProps> = (props) => {
admin: {
components: { afterNavLinks, beforeNavLinks },
},
} = config
} = payload.config
const BeforeNavLinks = Array.isArray(beforeNavLinks)
? beforeNavLinks.map((Component, i) => (
<WithServerSideProps Component={Component} key={i} payload={payload} />
))
: null
const AfterNavLinks = Array.isArray(afterNavLinks)
? afterNavLinks.map((Component, i) => (
<WithServerSideProps Component={Component} key={i} payload={payload} />
))
: null
return (
<NavWrapper baseClass={baseClass}>
<nav className={`${baseClass}__wrap`}>
{Array.isArray(beforeNavLinks) &&
beforeNavLinks.map((Component, i) => <Component key={i} />)}
{Array.isArray(BeforeNavLinks) && BeforeNavLinks.map((Component) => Component)}
<DefaultNavClient />
{Array.isArray(afterNavLinks) && afterNavLinks.map((Component, i) => <Component key={i} />)}
{Array.isArray(AfterNavLinks) && AfterNavLinks.map((Component) => Component)}
<div className={`${baseClass}__controls`}>
<Logout />
</div>

View File

@@ -1,20 +1,27 @@
import type { Payload } from 'payload'
import { WithServerSideProps } from '@payloadcms/ui/elements/WithServerSideProps'
import React from 'react'
export type RenderCustomComponentProps = {
CustomComponent?: React.ComponentType<any>
DefaultComponent: React.ComponentType<any>
componentProps?: Record<string, any>
/**
* Payload automatically gets added to the component if it's an RSC
*/
payload?: Payload
}
export const RenderCustomComponent: React.FC<RenderCustomComponentProps> = (props) => {
const { CustomComponent, DefaultComponent, componentProps = {} } = props
if (CustomComponent) {
return <CustomComponent {...componentProps} />
return <WithServerSideProps Component={CustomComponent} payload={null} {...componentProps} />
}
if (DefaultComponent) {
return <DefaultComponent {...componentProps} />
return <WithServerSideProps Component={DefaultComponent} payload={null} {...componentProps} />
}
return null

View File

@@ -36,8 +36,6 @@ export type Props = {
}) => void
}
import type { RelationshipFieldProps } from '@payloadcms/ui/fields/Relationship'
import { RenderCustomComponent } from '../../../elements/RenderCustomComponent/index.js'
import { useDebounce } from '../../../hooks/useDebounce.js'
import { Button } from '../../Button/index.js'

View File

@@ -1,4 +1,4 @@
import type { SanitizedConfig } from 'payload/config'
import type { Payload } from 'payload'
import React from 'react'
@@ -33,9 +33,9 @@ const PayloadLogo: React.FC = () => (
)
export const Logo: React.FC<{
config: SanitizedConfig
payload: Payload
}> = (props) => {
const { config } = props
const { payload } = props
const {
admin: {
@@ -45,7 +45,13 @@ export const Logo: React.FC<{
},
} = {},
} = {},
} = config
} = payload.config
return <RenderCustomComponent CustomComponent={CustomLogo} DefaultComponent={PayloadLogo} />
return (
<RenderCustomComponent
CustomComponent={CustomLogo}
DefaultComponent={PayloadLogo}
payload={payload}
/>
)
}

View File

@@ -1,4 +1,4 @@
import type { SanitizedConfig, VisibleEntities } from 'payload/types'
import type { Payload, SanitizedConfig, VisibleEntities } from 'payload/types'
import { EntityVisibilityProvider } from '@payloadcms/ui/providers/EntityVisibility'
import React from 'react'
@@ -18,28 +18,26 @@ const baseClass = 'template-default'
export type DefaultTemplateProps = {
children?: React.ReactNode
className?: string
config: Promise<SanitizedConfig> | SanitizedConfig
payload: Payload
visibleEntities?: VisibleEntities
}
export const DefaultTemplate: React.FC<DefaultTemplateProps> = async ({
export const DefaultTemplate: React.FC<DefaultTemplateProps> = ({
children,
className,
config: configPromise,
payload,
visibleEntities,
}) => {
const config = await configPromise
const {
admin: {
components: { Nav: CustomNav } = {
Nav: undefined,
},
} = {},
} = config || {}
} = payload.config || {}
const navProps: NavProps = {
config,
payload,
}
return (
@@ -55,6 +53,7 @@ export const DefaultTemplate: React.FC<DefaultTemplateProps> = async ({
CustomComponent={CustomNav}
DefaultComponent={DefaultNav}
componentProps={navProps}
payload={payload}
/>
<div className={`${baseClass}__wrap`}>
<AppHeader />

View File

@@ -1,3 +1,5 @@
import type { AdminViewProps } from 'payload/types'
import { DefaultTemplate } from '@payloadcms/ui/templates/Default'
import LinkImport from 'next/link.js'
import { redirect } from 'next/navigation.js'
@@ -18,15 +20,15 @@ export const CustomDefaultView: React.FC<AdminViewProps> = ({ initPageResult })
const {
permissions,
req: {
i18n,
payload,
payload: {
config,
config: {
routes: { admin: adminRoute },
},
},
user,
},
visibleEntities,
} = initPageResult
// If an unauthorized user tries to navigate straight to this page,
@@ -36,7 +38,7 @@ export const CustomDefaultView: React.FC<AdminViewProps> = ({ initPageResult })
}
return (
<DefaultTemplate config={config} i18n={i18n} permissions={permissions} user={user}>
<DefaultTemplate payload={payload} visibleEntities={visibleEntities}>
<SetStepNav
nav={[
{