fix: server edit view components don't receive document id prop (#13526)
### What? Make the document `id` available to server-rendered admin components that expect it—specifically `EditMenuItems` and `BeforeDocumentControls`—so `props.id` matches the official docs. ### Why? The docs show examples using `props.id`, but the runtime `serverProps` and TS types didn’t include it. This led to `undefined` at render time. ### How? - Add id to ServerProps and set it in renderDocumentSlots from req.routeParams.id. Fixes #13420
This commit is contained in:
@@ -293,7 +293,6 @@ Here's an example of a custom `editMenuItems` component:
|
|||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { PopupList } from '@payloadcms/ui'
|
|
||||||
|
|
||||||
import type { EditMenuItemsServerProps } from 'payload'
|
import type { EditMenuItemsServerProps } from 'payload'
|
||||||
|
|
||||||
@@ -301,12 +300,12 @@ export const EditMenuItems = async (props: EditMenuItemsServerProps) => {
|
|||||||
const href = `/custom-action?id=${props.id}`
|
const href = `/custom-action?id=${props.id}`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PopupList.ButtonGroup>
|
<>
|
||||||
<PopupList.Button href={href}>Custom Edit Menu Item</PopupList.Button>
|
<a href={href}>Custom Edit Menu Item</a>
|
||||||
<PopupList.Button href={href}>
|
<a href={href}>
|
||||||
Another Custom Edit Menu Item - add as many as you need!
|
Another Custom Edit Menu Item - add as many as you need!
|
||||||
</PopupList.Button>
|
</a>
|
||||||
</PopupList.ButtonGroup>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type {
|
import type {
|
||||||
BeforeDocumentControlsServerPropsOnly,
|
BeforeDocumentControlsServerPropsOnly,
|
||||||
DefaultServerFunctionArgs,
|
|
||||||
DocumentSlots,
|
DocumentSlots,
|
||||||
EditMenuItemsServerPropsOnly,
|
EditMenuItemsServerPropsOnly,
|
||||||
PayloadRequest,
|
PayloadRequest,
|
||||||
@@ -39,6 +38,7 @@ export const renderDocumentSlots: (args: {
|
|||||||
const isPreviewEnabled = collectionConfig?.admin?.preview || globalConfig?.admin?.preview
|
const isPreviewEnabled = collectionConfig?.admin?.preview || globalConfig?.admin?.preview
|
||||||
|
|
||||||
const serverProps: ServerProps = {
|
const serverProps: ServerProps = {
|
||||||
|
id: req.routeParams.id as number | string,
|
||||||
i18n: req.i18n,
|
i18n: req.i18n,
|
||||||
payload: req.payload,
|
payload: req.payload,
|
||||||
user: req.user,
|
user: req.user,
|
||||||
|
|||||||
@@ -402,6 +402,7 @@ export type Params = { [key: string]: string | string[] | undefined }
|
|||||||
export type ServerProps = {
|
export type ServerProps = {
|
||||||
readonly documentSubViewType?: DocumentSubViewTypes
|
readonly documentSubViewType?: DocumentSubViewTypes
|
||||||
readonly i18n: I18nClient
|
readonly i18n: I18nClient
|
||||||
|
readonly id?: number | string
|
||||||
readonly locale?: Locale
|
readonly locale?: Locale
|
||||||
readonly params?: Params
|
readonly params?: Params
|
||||||
readonly payload: Payload
|
readonly payload: Payload
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { CollectionConfig } from 'payload'
|
import type { CollectionConfig } from 'payload'
|
||||||
|
|
||||||
import { editMenuItemsSlug, postsCollectionSlug, uploadCollectionSlug } from '../slugs.js'
|
import { editMenuItemsSlug } from '../slugs.js'
|
||||||
|
|
||||||
export const EditMenuItems: CollectionConfig = {
|
export const EditMenuItems: CollectionConfig = {
|
||||||
slug: editMenuItemsSlug,
|
slug: editMenuItemsSlug,
|
||||||
@@ -11,6 +11,9 @@ export const EditMenuItems: CollectionConfig = {
|
|||||||
{
|
{
|
||||||
path: '/components/EditMenuItems/index.js#EditMenuItems',
|
path: '/components/EditMenuItems/index.js#EditMenuItems',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/components/EditMenuItemsServer/index.js#EditMenuItemsServer',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
13
test/admin/components/EditMenuItemsServer/index.tsx
Normal file
13
test/admin/components/EditMenuItemsServer/index.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import type { EditMenuItemsServerProps } from 'payload'
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export const EditMenuItemsServer = (props: EditMenuItemsServerProps) => {
|
||||||
|
const href = `/custom-action?id=${props.id}`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<a href={href}>Custom Edit Menu Item (Server)</a>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -731,7 +731,7 @@ describe('Document View', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('custom editMenuItem components', () => {
|
describe('custom editMenuItem components', () => {
|
||||||
test('should render custom editMenuItems component', async () => {
|
test('should render custom editMenuItems client component', async () => {
|
||||||
await page.goto(editMenuItemsURL.create)
|
await page.goto(editMenuItemsURL.create)
|
||||||
await page.locator('#field-title')?.fill(title)
|
await page.locator('#field-title')?.fill(title)
|
||||||
await saveDocAndAssert(page)
|
await saveDocAndAssert(page)
|
||||||
@@ -746,6 +746,51 @@ describe('Document View', () => {
|
|||||||
|
|
||||||
await expect(customEditMenuItem).toBeVisible()
|
await expect(customEditMenuItem).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should render custom editMenuItems server component', async () => {
|
||||||
|
await page.goto(editMenuItemsURL.create)
|
||||||
|
await page.locator('#field-title')?.fill(title)
|
||||||
|
await saveDocAndAssert(page)
|
||||||
|
|
||||||
|
const threeDotMenu = page.getByRole('main').locator('.doc-controls__popup')
|
||||||
|
await expect(threeDotMenu).toBeVisible()
|
||||||
|
await threeDotMenu.click()
|
||||||
|
|
||||||
|
const popup = page.locator('.popup--active .popup__content')
|
||||||
|
await expect(popup).toBeVisible()
|
||||||
|
|
||||||
|
const customEditMenuItem = popup.getByRole('link', {
|
||||||
|
name: 'Custom Edit Menu Item (Server)',
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(customEditMenuItem).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should render doc id in href of custom editMenuItems server component link', async () => {
|
||||||
|
await page.goto(editMenuItemsURL.create)
|
||||||
|
await page.locator('#field-title')?.fill(title)
|
||||||
|
await saveDocAndAssert(page)
|
||||||
|
|
||||||
|
const threeDotMenu = page.getByRole('main').locator('.doc-controls__popup')
|
||||||
|
await expect(threeDotMenu).toBeVisible()
|
||||||
|
await threeDotMenu.click()
|
||||||
|
|
||||||
|
const popup = page.locator('.popup--active .popup__content')
|
||||||
|
await expect(popup).toBeVisible()
|
||||||
|
|
||||||
|
const customEditMenuItem = popup.getByRole('link', {
|
||||||
|
name: 'Custom Edit Menu Item (Server)',
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(customEditMenuItem).toBeVisible()
|
||||||
|
|
||||||
|
// Extract the document id from the edit page URL (last path segment)
|
||||||
|
const editPath = new URL(page.url()).pathname
|
||||||
|
const docId = editPath.split('/').filter(Boolean).pop()!
|
||||||
|
|
||||||
|
// Assert the href contains the same id
|
||||||
|
await expect(customEditMenuItem).toHaveAttribute('href', `/custom-action?id=${docId}`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user