fix(next): prevent errors in globals version view (#12920)

### What?
This PR fixes a runtime error that occurs when opening the "More
versions..." drawer while browsing the versions for a global. It also
fixes a minor runtime error when navigating to a global version view
where an optional chaining operator was missing as the collection
variable would be undefined as we are viewing a global.

This PR also adds an e2e test to ensure the versions drawer is
accessible and renders the appropriate number of versions for globals.

### Why?
To properly render global version views without errors.

### How?
By threading the global slug to the versions drawer and adjusting some
properties of the `renderDocument` server function call there. This PR
also adds an optional chaining operator the `versionUseAsTitle` in the
original view to prevent an error in globals.

Notes:
- This was brought to my attention in Discord by a handful of users

Before: (Missing optional chaining error)


[error1-verions-Editing---Menu---Payload.webm](https://github.com/user-attachments/assets/3dc4dbe4-ee5a-43df-8d25-05128b05e063)

Before: (Versions drawer error)


[error2-versions-Editing---Menu---Payload.webm](https://github.com/user-attachments/assets/98c3e1da-cb0b-4a36-bafd-240f641e8814)


After:


[versions-globals-Dashboard---Payload.webm](https://github.com/user-attachments/assets/c778d3f0-a8fe-4e31-92cb-62da8e6d8cb4)
This commit is contained in:
Said Akhrarov
2025-06-24 13:18:25 -04:00
committed by GitHub
parent 886c07e918
commit 39e95195e1
6 changed files with 89 additions and 18 deletions

View File

@@ -238,6 +238,7 @@ export const DefaultVersionView: React.FC<DefaultVersionsViewProps> = ({
<SelectComparison
collectionSlug={collectionSlug}
docID={originalDocID}
globalSlug={globalSlug}
onChange={onChangeVersionFrom}
versionFromID={versionFromID}
versionFromOptions={versionFromOptions}

View File

@@ -24,11 +24,12 @@ export const formatVersionDrawerSlug = ({
}) => `version-drawer_${depth}_${uuid}`
export const VersionDrawerContent: React.FC<{
collectionSlug: string
docID: number | string
collectionSlug?: string
docID?: number | string
drawerSlug: string
globalSlug?: string
}> = (props) => {
const { collectionSlug, docID, drawerSlug } = props
const { collectionSlug, docID, drawerSlug, globalSlug } = props
const { closeModal } = useModal()
const searchParams = useSearchParams()
const prevSearchParams = useRef(searchParams)
@@ -46,12 +47,20 @@ export const VersionDrawerContent: React.FC<{
setIsLoading(true)
try {
const isGlobal = Boolean(globalSlug)
const entitySlug = collectionSlug ?? globalSlug
const result = await renderDocument({
collectionSlug,
collectionSlug: entitySlug,
docID,
drawerSlug,
paramsOverride: {
segments: ['collections', collectionSlug, String(docID), 'versions'],
segments: [
isGlobal ? 'globals' : 'collections',
entitySlug,
isGlobal ? undefined : String(docID),
'versions',
].filter(Boolean),
},
redirectAfterDelete: false,
redirectAfterDuplicate: false,
@@ -75,7 +84,7 @@ export const VersionDrawerContent: React.FC<{
void fetchDocumentView()
},
[closeModal, collectionSlug, drawerSlug, renderDocument, searchParams, t],
[closeModal, collectionSlug, globalSlug, drawerSlug, renderDocument, searchParams, t],
)
useEffect(() => {
@@ -93,11 +102,12 @@ export const VersionDrawerContent: React.FC<{
return DocumentView
}
export const VersionDrawer: React.FC<{
collectionSlug: string
docID: number | string
collectionSlug?: string
docID?: number | string
drawerSlug: string
globalSlug?: string
}> = (props) => {
const { collectionSlug, docID, drawerSlug } = props
const { collectionSlug, docID, drawerSlug, globalSlug } = props
const { t } = useTranslation()
return (
@@ -107,7 +117,12 @@ export const VersionDrawer: React.FC<{
slug={drawerSlug}
title={t('version:selectVersionToCompare')}
>
<VersionDrawerContent collectionSlug={collectionSlug} docID={docID} drawerSlug={drawerSlug} />
<VersionDrawerContent
collectionSlug={collectionSlug}
docID={docID}
drawerSlug={drawerSlug}
globalSlug={globalSlug}
/>
</Drawer>
)
}
@@ -115,9 +130,11 @@ export const VersionDrawer: React.FC<{
export const useVersionDrawer = ({
collectionSlug,
docID,
globalSlug,
}: {
collectionSlug: string
docID: number | string
collectionSlug?: string
docID?: number | string
globalSlug?: string
}) => {
const drawerDepth = useEditDepth()
const uuid = useId()
@@ -147,9 +164,14 @@ export const useVersionDrawer = ({
const MemoizedDrawer = useMemo(() => {
return () => (
<VersionDrawer collectionSlug={collectionSlug} docID={docID} drawerSlug={drawerSlug} />
<VersionDrawer
collectionSlug={collectionSlug}
docID={docID}
drawerSlug={drawerSlug}
globalSlug={globalSlug}
/>
)
}, [collectionSlug, docID, drawerSlug])
}, [collectionSlug, docID, drawerSlug, globalSlug])
return useMemo(
() => ({

View File

@@ -17,13 +17,14 @@ export const SelectComparison: React.FC<Props> = memo((props) => {
const {
collectionSlug,
docID,
globalSlug,
onChange: onChangeFromProps,
versionFromID,
versionFromOptions,
} = props
const { t } = useTranslation()
const { Drawer, openDrawer } = useVersionDrawer({ collectionSlug, docID })
const { Drawer, openDrawer } = useVersionDrawer({ collectionSlug, docID, globalSlug })
const options = useMemo(() => {
return [

View File

@@ -3,8 +3,9 @@ import type { PaginatedDocs, SanitizedCollectionConfig } from 'payload'
import type { CompareOption } from '../Default/types.js'
export type Props = {
collectionSlug: string
docID: number | string
collectionSlug?: string
docID?: number | string
globalSlug?: string
onChange: (val: CompareOption) => void
versionFromID?: string
versionFromOptions: CompareOption[]

View File

@@ -425,7 +425,7 @@ export async function VersionView(props: DocumentViewServerProps) {
VersionToCreatedAtLabel={formatPill({ doc: versionTo, labelStyle: 'pill' })}
versionToID={versionTo.id}
versionToStatus={versionTo.version?._status}
versionToUseAsTitle={versionTo[collectionConfig.admin?.useAsTitle || 'id']}
versionToUseAsTitle={versionTo[collectionConfig?.admin?.useAsTitle || 'id']}
/>
)
}

View File

@@ -205,6 +205,7 @@ describe('Versions', () => {
const fieldValue = autosaveRelationField.locator('.value-container')
await expect(fieldValue).toContainText('test')
})
test('should show collection versions view level action in collection versions view', async () => {
await page.goto(url.list)
await page.locator('tbody tr .cell-title a').first().click()
@@ -839,6 +840,51 @@ describe('Versions', () => {
await page.goto(url.global(disablePublishGlobalSlug))
await expect(page.locator('#action-save')).not.toBeAttached()
})
test('global — should show versions drawer when SelectComparison more option is clicked', async () => {
await payload.updateGlobal({
slug: draftGlobalSlug,
data: {
title: 'initial title',
},
})
await payload.updateGlobal({
slug: draftGlobalSlug,
data: {
title: 'initial title 2',
},
})
const url = new AdminUrlUtil(serverURL, draftGlobalSlug)
await page.goto(`${url.global(draftGlobalSlug)}/versions`)
const versionsTable = page.locator('.table table')
await expect(versionsTable).toBeVisible()
const versionAnchor = versionsTable.locator('tbody tr.row-1 td.cell-updatedAt a')
await expect(versionAnchor).toBeVisible()
await versionAnchor.click()
const compareFromContainer = page.locator(
'.view-version__version-from .field-type.compare-version',
)
await expect(compareFromContainer).toBeVisible()
const fromSelect = compareFromContainer.locator('.react-select .rs__control')
await expect(fromSelect).toBeVisible()
await fromSelect.click()
const moreVersions = compareFromContainer.locator('.rs__option:has-text("More versions...")')
await expect(moreVersions).toBeVisible()
await moreVersions.click()
const versionDrawer = page.locator('dialog.version-drawer')
await expect(versionDrawer).toBeVisible()
const versionsDrawerTableBody = versionDrawer.locator('main.versions table tbody')
await expect(versionsDrawerTableBody).toBeVisible()
await expect(versionsDrawerTableBody.locator('tr')).toHaveCount(2)
})
})
describe('Scheduled publish', () => {