fix(ui): fix version list status for unpublished documents (#11983)

### What?
Fixes the label for documents which were the current published document
but got unpublished in the version view.

### Why?
If the most recent published document was unpublished, it remained
displayed as "Currently published version" in the version list.

### How?
Checks whether the document has a currently published version instead of
only looking at the latest published version when determining the label
in the versions view.

Fixes https://github.com/payloadcms/payload/issues/10838

---------

Co-authored-by: Alessio Gravili <alessio@gravili.de>
Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
This commit is contained in:
Tobias Odendahl
2025-05-02 15:21:02 +02:00
committed by GitHub
parent 5365d4f1c2
commit b6b02ac97c
3 changed files with 151 additions and 8 deletions

View File

@@ -2,7 +2,14 @@
import type { PaginatedDocs, Where } from 'payload' import type { PaginatedDocs, Where } from 'payload'
import { fieldBaseClass, Pill, ReactSelect, useConfig, useTranslation } from '@payloadcms/ui' import {
fieldBaseClass,
Pill,
ReactSelect,
useConfig,
useDocumentInfo,
useTranslation,
} from '@payloadcms/ui'
import { formatDate } from '@payloadcms/ui/shared' import { formatDate } from '@payloadcms/ui/shared'
import { stringify } from 'qs-esm' import { stringify } from 'qs-esm'
import React, { useCallback, useEffect, useState } from 'react' import React, { useCallback, useEffect, useState } from 'react'
@@ -37,6 +44,8 @@ export const SelectComparison: React.FC<Props> = (props) => {
}, },
} = useConfig() } = useConfig()
const { hasPublishedDoc } = useDocumentInfo()
const [options, setOptions] = useState< const [options, setOptions] = useState<
{ {
label: React.ReactNode | string label: React.ReactNode | string
@@ -109,7 +118,10 @@ export const SelectComparison: React.FC<Props> = (props) => {
}, },
published: { published: {
currentLabel: t('version:currentPublishedVersion'), currentLabel: t('version:currentPublishedVersion'),
latestVersion: latestPublishedVersion, // The latest published version does not necessarily equal the current published version,
// because the latest published version might have been unpublished in the meantime.
// Hence, we should only use the latest published version if there is a published document.
latestVersion: hasPublishedDoc ? latestPublishedVersion : undefined,
pillStyle: 'success', pillStyle: 'success',
previousLabel: t('version:previouslyPublished'), previousLabel: t('version:previouslyPublished'),
}, },

View File

@@ -85,13 +85,34 @@ export async function VersionsView(props: DocumentViewServerProps) {
payload, payload,
status: 'draft', status: 'draft',
}) })
latestPublishedVersion = await getLatestVersion({ const publishedDoc = await payload.count({
slug: collectionSlug, collection: collectionSlug,
type: 'collection', depth: 0,
parentID: id, overrideAccess: true,
payload, req,
status: 'published', where: {
id: {
equals: id,
},
_status: {
equals: 'published',
},
},
}) })
// If we pass a latestPublishedVersion to buildVersionColumns,
// this will be used to display it as the "current published version".
// However, the latest published version might have been unpublished in the meantime.
// Hence, we should only pass the latest published version if there is a published document.
latestPublishedVersion =
publishedDoc.totalDocs > 0 &&
(await getLatestVersion({
slug: collectionSlug,
type: 'collection',
parentID: id,
payload,
status: 'published',
}))
} }
} catch (err) { } catch (err) {
logError({ err, payload }) logError({ err, payload })

View File

@@ -205,6 +205,116 @@ describe('Versions', () => {
await expect(page.locator('#field-title')).toHaveValue('v1') await expect(page.locator('#field-title')).toHaveValue('v1')
}) })
test('should show currently published version status in versions view', async () => {
const publishedDoc = await payload.create({
collection: draftCollectionSlug,
data: {
_status: 'published',
title: 'title',
description: 'description',
},
overrideAccess: true,
})
await page.goto(`${url.edit(publishedDoc.id)}/versions`)
await expect(page.locator('main.versions')).toContainText('Current Published Version')
})
test('should show unpublished version status in versions view', async () => {
const publishedDoc = await payload.create({
collection: draftCollectionSlug,
data: {
_status: 'published',
title: 'title',
description: 'description',
},
overrideAccess: true,
})
// Unpublish the document
await payload.update({
collection: draftCollectionSlug,
id: publishedDoc.id,
data: {
_status: 'draft',
},
draft: false,
})
await page.goto(`${url.edit(publishedDoc.id)}/versions`)
await expect(page.locator('main.versions')).toContainText('Previously Published')
})
test('should show global versions view level action in globals versions view', async () => {
const global = new AdminUrlUtil(serverURL, draftGlobalSlug)
await page.goto(`${global.global(draftGlobalSlug)}/versions`)
await expect(page.locator('.app-header .global-versions-button')).toHaveCount(1)
})
test('global — has versions tab', async () => {
const global = new AdminUrlUtil(serverURL, draftGlobalSlug)
await page.goto(global.global(draftGlobalSlug))
const docURL = page.url()
const pathname = new URL(docURL).pathname
const versionsTab = page.locator('.doc-tab', {
hasText: 'Versions',
})
await versionsTab.waitFor({ state: 'visible' })
expect(versionsTab).toBeTruthy()
const href = versionsTab.locator('a').first()
await expect(href).toHaveAttribute('href', `${pathname}/versions`)
})
test('global — respects max number of versions', async () => {
await payload.updateGlobal({
slug: draftWithMaxGlobalSlug,
data: {
title: 'initial title',
},
})
const global = new AdminUrlUtil(serverURL, draftWithMaxGlobalSlug)
await page.goto(global.global(draftWithMaxGlobalSlug))
const titleFieldInitial = page.locator('#field-title')
await titleFieldInitial.fill('updated title')
await saveDocAndAssert(page, '#action-save-draft')
await expect(titleFieldInitial).toHaveValue('updated title')
const versionsTab = page.locator('.doc-tab', {
hasText: '1',
})
await versionsTab.waitFor({ state: 'visible' })
expect(versionsTab).toBeTruthy()
const titleFieldUpdated = page.locator('#field-title')
await titleFieldUpdated.fill('latest title')
await saveDocAndAssert(page, '#action-save-draft')
await expect(titleFieldUpdated).toHaveValue('latest title')
const versionsTabUpdated = page.locator('.doc-tab', {
hasText: '1',
})
await versionsTabUpdated.waitFor({ state: 'visible' })
expect(versionsTabUpdated).toBeTruthy()
})
test('global — has versions route', async () => {
const global = new AdminUrlUtil(serverURL, autoSaveGlobalSlug)
const versionsURL = `${global.global(autoSaveGlobalSlug)}/versions`
await page.goto(versionsURL)
await expect(() => {
expect(page.url()).toMatch(/\/versions/)
}).toPass({ timeout: 10000, intervals: [100] })
})
test('collection - should autosave', async () => { test('collection - should autosave', async () => {
await page.goto(autosaveURL.create) await page.goto(autosaveURL.create)
await page.locator('#field-title').fill('autosave title') await page.locator('#field-title').fill('autosave title')