From 96074530b1106b521c1c6b28c8ed3659b45f1e34 Mon Sep 17 00:00:00 2001 From: Patrik <35232443+PatrikKozak@users.noreply.github.com> Date: Thu, 21 Aug 2025 13:23:51 -0400 Subject: [PATCH] fix: server edit view components don't receive document id prop (#13526) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### 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 --- docs/custom-components/edit-view.mdx | 11 ++--- .../views/Document/renderDocumentSlots.tsx | 2 +- packages/payload/src/config/types.ts | 1 + test/admin/collections/editMenuItems.ts | 5 +- .../components/EditMenuItemsServer/index.tsx | 13 +++++ test/admin/e2e/document-view/e2e.spec.ts | 47 ++++++++++++++++++- 6 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 test/admin/components/EditMenuItemsServer/index.tsx diff --git a/docs/custom-components/edit-view.mdx b/docs/custom-components/edit-view.mdx index 92a4e3a778..af7f371ce5 100644 --- a/docs/custom-components/edit-view.mdx +++ b/docs/custom-components/edit-view.mdx @@ -293,7 +293,6 @@ Here's an example of a custom `editMenuItems` component: ```tsx import React from 'react' -import { PopupList } from '@payloadcms/ui' import type { EditMenuItemsServerProps } from 'payload' @@ -301,12 +300,12 @@ export const EditMenuItems = async (props: EditMenuItemsServerProps) => { const href = `/custom-action?id=${props.id}` return ( - - Custom Edit Menu Item - + <> + Custom Edit Menu Item + Another Custom Edit Menu Item - add as many as you need! - - + + ) } ``` diff --git a/packages/next/src/views/Document/renderDocumentSlots.tsx b/packages/next/src/views/Document/renderDocumentSlots.tsx index 8d5f7dbcc5..7b81e3daef 100644 --- a/packages/next/src/views/Document/renderDocumentSlots.tsx +++ b/packages/next/src/views/Document/renderDocumentSlots.tsx @@ -1,6 +1,5 @@ import type { BeforeDocumentControlsServerPropsOnly, - DefaultServerFunctionArgs, DocumentSlots, EditMenuItemsServerPropsOnly, PayloadRequest, @@ -39,6 +38,7 @@ export const renderDocumentSlots: (args: { const isPreviewEnabled = collectionConfig?.admin?.preview || globalConfig?.admin?.preview const serverProps: ServerProps = { + id: req.routeParams.id as number | string, i18n: req.i18n, payload: req.payload, user: req.user, diff --git a/packages/payload/src/config/types.ts b/packages/payload/src/config/types.ts index fdd2e460f6..82c6f132c9 100644 --- a/packages/payload/src/config/types.ts +++ b/packages/payload/src/config/types.ts @@ -402,6 +402,7 @@ export type Params = { [key: string]: string | string[] | undefined } export type ServerProps = { readonly documentSubViewType?: DocumentSubViewTypes readonly i18n: I18nClient + readonly id?: number | string readonly locale?: Locale readonly params?: Params readonly payload: Payload diff --git a/test/admin/collections/editMenuItems.ts b/test/admin/collections/editMenuItems.ts index f08f303528..278318c0d8 100644 --- a/test/admin/collections/editMenuItems.ts +++ b/test/admin/collections/editMenuItems.ts @@ -1,6 +1,6 @@ import type { CollectionConfig } from 'payload' -import { editMenuItemsSlug, postsCollectionSlug, uploadCollectionSlug } from '../slugs.js' +import { editMenuItemsSlug } from '../slugs.js' export const EditMenuItems: CollectionConfig = { slug: editMenuItemsSlug, @@ -11,6 +11,9 @@ export const EditMenuItems: CollectionConfig = { { path: '/components/EditMenuItems/index.js#EditMenuItems', }, + { + path: '/components/EditMenuItemsServer/index.js#EditMenuItemsServer', + }, ], }, }, diff --git a/test/admin/components/EditMenuItemsServer/index.tsx b/test/admin/components/EditMenuItemsServer/index.tsx new file mode 100644 index 0000000000..3bd5aaa334 --- /dev/null +++ b/test/admin/components/EditMenuItemsServer/index.tsx @@ -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 ( +
+ Custom Edit Menu Item (Server) +
+ ) +} diff --git a/test/admin/e2e/document-view/e2e.spec.ts b/test/admin/e2e/document-view/e2e.spec.ts index a4705ae0ed..f793332a86 100644 --- a/test/admin/e2e/document-view/e2e.spec.ts +++ b/test/admin/e2e/document-view/e2e.spec.ts @@ -731,7 +731,7 @@ describe('Document View', () => { }) 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.locator('#field-title')?.fill(title) await saveDocAndAssert(page) @@ -746,6 +746,51 @@ describe('Document View', () => { 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}`) + }) }) })