fix(next): threads server props to custom providers (#9412)

When defining custom providers as server components, they currently do
not receive any of the server props that custom components expect to
receive, like `payload`, `i18n`, `user`, and so on.
This commit is contained in:
Jacob Fletcher
2024-11-21 15:44:32 -05:00
committed by GitHub
parent 2036a566fd
commit f5683b0a64
6 changed files with 63 additions and 8 deletions

View File

@@ -1,4 +1,4 @@
import type { Config, ImportMap } from 'payload' import type { Config, ImportMap, ServerProps } from 'payload'
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent' import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'
import '@payloadcms/ui/scss/app.scss' import '@payloadcms/ui/scss/app.scss'
@@ -8,14 +8,24 @@ type Args = {
readonly children: React.ReactNode readonly children: React.ReactNode
readonly importMap: ImportMap readonly importMap: ImportMap
readonly providers: Config['admin']['components']['providers'] readonly providers: Config['admin']['components']['providers']
readonly serverProps: ServerProps
} }
export function NestProviders({ children, importMap, providers }: Args): React.ReactNode { export function NestProviders({
children,
importMap,
providers,
serverProps,
}: Args): React.ReactNode {
return RenderServerComponent({ return RenderServerComponent({
clientProps: { clientProps: {
children: children:
providers.length > 1 ? ( providers.length > 1 ? (
<NestProviders importMap={importMap} providers={providers.slice(1)}> <NestProviders
importMap={importMap}
providers={providers.slice(1)}
serverProps={serverProps}
>
{children} {children}
</NestProviders> </NestProviders>
) : ( ) : (
@@ -24,5 +34,6 @@ export function NestProviders({ children, importMap, providers }: Args): React.R
}, },
Component: providers[0], Component: providers[0],
importMap, importMap,
serverProps,
}) })
} }

View File

@@ -54,7 +54,7 @@ export const RootLayout = async ({
const payload = await getPayload({ config, importMap }) const payload = await getPayload({ config, importMap })
const { i18n, permissions, user } = await initReq(config) const { i18n, permissions, req, user } = await initReq(config)
const dir = (rtlLanguages as unknown as AcceptedLanguages[]).includes(languageCode) const dir = (rtlLanguages as unknown as AcceptedLanguages[]).includes(languageCode)
? 'RTL' ? 'RTL'
@@ -117,6 +117,12 @@ export const RootLayout = async ({
<NestProviders <NestProviders
importMap={payload.importMap} importMap={payload.importMap}
providers={config.admin?.components?.providers} providers={config.admin?.components?.providers}
serverProps={{
i18n,
payload,
permissions,
user,
}}
> >
{children} {children}
</NestProviders> </NestProviders>

View File

@@ -17,9 +17,14 @@ export const CustomProvider: React.FC<{ children: React.ReactNode }> = ({ childr
setCustom, setCustom,
} }
console.log('custom provider called') return (
<Context.Provider value={value}>
return <Context.Provider value={value}>{children}</Context.Provider> <div className="custom-provider" style={{ display: 'none' }}>
This is a custom provider.
</div>
{children}
</Context.Provider>
)
} }
export const useCustom = () => useContext(Context) export const useCustom = () => useContext(Context)

View File

@@ -0,0 +1,17 @@
import type { ServerProps } from 'payload'
import React, { Fragment } from 'react'
export const CustomProviderServer: React.FC<{ children: React.ReactNode } & ServerProps> = ({
children,
payload,
}) => {
return (
<Fragment>
<div className="custom-provider-server" style={{ display: 'none' }}>
{`This is a custom provider with payload: ${Boolean(payload)}`}
</div>
{children}
</Fragment>
)
}

View File

@@ -61,7 +61,7 @@ export default buildConfigWithDefaults({
Button: '/components/Logout/index.js#Logout', Button: '/components/Logout/index.js#Logout',
}, },
providers: [ providers: [
'/components/CustomProvider/index.js#CustomProvider', '/components/CustomProviderServer/index.js#CustomProviderServer',
'/components/CustomProvider/index.js#CustomProvider', '/components/CustomProvider/index.js#CustomProvider',
], ],
views: { views: {

View File

@@ -460,6 +460,22 @@ describe('admin1', () => {
}) })
}) })
describe('custom providers', () => {
test('should render custom providers', async () => {
await page.goto(`${serverURL}/admin`)
await expect(page.locator('.custom-provider')).toHaveCount(1)
await expect(page.locator('.custom-provider')).toContainText('This is a custom provider.')
})
test('should render custom provider server components with props', async () => {
await page.goto(`${serverURL}/admin`)
await expect(page.locator('.custom-provider-server')).toHaveCount(1)
await expect(page.locator('.custom-provider-server')).toContainText(
'This is a custom provider with payload: true',
)
})
})
describe('custom views', () => { describe('custom views', () => {
test('root — should render custom view', async () => { test('root — should render custom view', async () => {
await page.goto(`${serverURL}${adminRoutes.routes.admin}${customViewPath}`) await page.goto(`${serverURL}${adminRoutes.routes.admin}${customViewPath}`)