chore: merge conflict
This commit is contained in:
@@ -69,6 +69,12 @@ export const Posts: CollectionConfig = {
|
||||
},
|
||||
},
|
||||
],
|
||||
edit: {
|
||||
beforeDocumentControls: [
|
||||
'/components/BeforeDocumentControls/CustomDraftButton/index.js#CustomDraftButton',
|
||||
'/components/BeforeDocumentControls/CustomSaveButton/index.js#CustomSaveButton',
|
||||
],
|
||||
},
|
||||
},
|
||||
pagination: {
|
||||
defaultLimit: 5,
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { BeforeDocumentControlsServerProps } from 'payload'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
const baseClass = 'custom-draft-button'
|
||||
|
||||
export function CustomDraftButton(props: BeforeDocumentControlsServerProps) {
|
||||
return (
|
||||
<div
|
||||
className={baseClass}
|
||||
id="custom-draft-button"
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 'calc(var(--base) / 4)',
|
||||
}}
|
||||
>
|
||||
<p className="nav__label" style={{ color: 'var(--theme-text)', margin: 0 }}>
|
||||
Custom Draft Button
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { BeforeDocumentControlsServerProps } from 'payload'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
const baseClass = 'custom-save-button'
|
||||
|
||||
export function CustomSaveButton(props: BeforeDocumentControlsServerProps) {
|
||||
return (
|
||||
<div
|
||||
className={baseClass}
|
||||
id="custom-save-button"
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 'calc(var(--base) / 4)',
|
||||
}}
|
||||
>
|
||||
<p className="nav__label" style={{ color: 'var(--theme-text)', margin: 0 }}>
|
||||
Custom Save Button
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -117,7 +117,7 @@ describe('Document View', () => {
|
||||
})
|
||||
expect(collectionItems.docs.length).toBe(1)
|
||||
await page.goto(
|
||||
`${postsUrl.collection(noApiViewGlobalSlug)}/${collectionItems.docs[0].id}/api`,
|
||||
`${postsUrl.collection(noApiViewGlobalSlug)}/${collectionItems?.docs[0]?.id}/api`,
|
||||
)
|
||||
await expect(page.locator('.not-found')).toHaveCount(1)
|
||||
})
|
||||
@@ -340,20 +340,32 @@ describe('Document View', () => {
|
||||
await navigateToDoc(page, postsUrl)
|
||||
await page.locator('#field-title').fill(title)
|
||||
await saveDocAndAssert(page)
|
||||
|
||||
await page
|
||||
.locator('.field-type.relationship .relationship--single-value__drawer-toggler')
|
||||
.click()
|
||||
|
||||
await wait(500)
|
||||
|
||||
const drawer1Content = page.locator('[id^=doc-drawer_posts_1_] .drawer__content')
|
||||
await expect(drawer1Content).toBeVisible()
|
||||
const drawerLeft = await drawer1Content.boundingBox().then((box) => box.x)
|
||||
|
||||
const drawer1Box = await drawer1Content.boundingBox()
|
||||
await expect.poll(() => drawer1Box).not.toBeNull()
|
||||
const drawerLeft = drawer1Box!.x
|
||||
|
||||
await drawer1Content
|
||||
.locator('.field-type.relationship .relationship--single-value__drawer-toggler')
|
||||
.click()
|
||||
|
||||
const drawer2Content = page.locator('[id^=doc-drawer_posts_2_] .drawer__content')
|
||||
await expect(drawer2Content).toBeVisible()
|
||||
const drawer2Left = await drawer2Content.boundingBox().then((box) => box.x)
|
||||
expect(drawer2Left > drawerLeft).toBe(true)
|
||||
|
||||
const drawer2Box = await drawer2Content.boundingBox()
|
||||
await expect.poll(() => drawer2Box).not.toBeNull()
|
||||
const drawer2Left = drawer2Box!.x
|
||||
|
||||
await expect.poll(() => drawer2Left > drawerLeft).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -616,6 +628,24 @@ describe('Document View', () => {
|
||||
await expect(notFound).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
describe('custom document controls', () => {
|
||||
test('should show custom elements in document controls in collection', async () => {
|
||||
await page.goto(postsUrl.create)
|
||||
const customDraftButton = page.locator('#custom-draft-button')
|
||||
const customSaveButton = page.locator('#custom-save-button')
|
||||
|
||||
await expect(customDraftButton).toBeVisible()
|
||||
await expect(customSaveButton).toBeVisible()
|
||||
})
|
||||
|
||||
test('should show custom elements in document controls in global', async () => {
|
||||
await page.goto(globalURL.global(globalSlug))
|
||||
const customDraftButton = page.locator('#custom-draft-button')
|
||||
|
||||
await expect(customDraftButton).toBeVisible()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
async function createPost(overrides?: Partial<Post>): Promise<Post> {
|
||||
|
||||
@@ -6,6 +6,11 @@ export const Global: GlobalConfig = {
|
||||
slug: globalSlug,
|
||||
admin: {
|
||||
components: {
|
||||
elements: {
|
||||
beforeDocumentControls: [
|
||||
'/components/BeforeDocumentControls/CustomDraftButton/index.js#CustomDraftButton',
|
||||
],
|
||||
},
|
||||
views: {
|
||||
edit: {
|
||||
api: {
|
||||
|
||||
@@ -1218,7 +1218,6 @@ describe('collections-rest', () => {
|
||||
.GET(`/${pointSlug}`, {
|
||||
query: {
|
||||
limit: 5,
|
||||
sort: 'point',
|
||||
where: {
|
||||
point: {
|
||||
// querying large enough range to include all docs
|
||||
|
||||
@@ -471,6 +471,16 @@ export default buildConfigWithDefaults({
|
||||
type: 'text',
|
||||
virtual: 'post.category.title',
|
||||
},
|
||||
{
|
||||
name: 'postCategoryID',
|
||||
type: 'json',
|
||||
virtual: 'post.category.id',
|
||||
},
|
||||
{
|
||||
name: 'postID',
|
||||
type: 'json',
|
||||
virtual: 'post.id',
|
||||
},
|
||||
{
|
||||
name: 'postLocalized',
|
||||
type: 'text',
|
||||
@@ -481,6 +491,16 @@ export default buildConfigWithDefaults({
|
||||
type: 'relationship',
|
||||
relationTo: 'posts',
|
||||
},
|
||||
{
|
||||
name: 'customID',
|
||||
type: 'relationship',
|
||||
relationTo: 'custom-ids',
|
||||
},
|
||||
{
|
||||
name: 'customIDValue',
|
||||
type: 'text',
|
||||
virtual: 'customID.id',
|
||||
},
|
||||
],
|
||||
versions: { drafts: true },
|
||||
},
|
||||
|
||||
@@ -1993,11 +1993,63 @@ describe('database', () => {
|
||||
collection: 'virtual-relations',
|
||||
depth: 0,
|
||||
where: { id: { equals: id } },
|
||||
draft: true,
|
||||
})
|
||||
expect(draft.docs[0]?.postTitle).toBe('my-title')
|
||||
})
|
||||
|
||||
it('should allow virtual field as reference to ID', async () => {
|
||||
const post = await payload.create({ collection: 'posts', data: { title: 'my-title' } })
|
||||
const { id } = await payload.create({
|
||||
collection: 'virtual-relations',
|
||||
depth: 0,
|
||||
data: { post: post.id },
|
||||
})
|
||||
|
||||
const docDepth2 = await payload.findByID({ collection: 'virtual-relations', id })
|
||||
expect(docDepth2.postID).toBe(post.id)
|
||||
const docDepth0 = await payload.findByID({ collection: 'virtual-relations', id, depth: 0 })
|
||||
expect(docDepth0.postID).toBe(post.id)
|
||||
})
|
||||
|
||||
it('should allow virtual field as reference to custom ID', async () => {
|
||||
const customID = await payload.create({ collection: 'custom-ids', data: {} })
|
||||
const { id } = await payload.create({
|
||||
collection: 'virtual-relations',
|
||||
depth: 0,
|
||||
data: { customID: customID.id },
|
||||
})
|
||||
|
||||
const docDepth2 = await payload.findByID({ collection: 'virtual-relations', id })
|
||||
expect(docDepth2.customIDValue).toBe(customID.id)
|
||||
const docDepth0 = await payload.findByID({
|
||||
collection: 'virtual-relations',
|
||||
id,
|
||||
depth: 0,
|
||||
})
|
||||
expect(docDepth0.customIDValue).toBe(customID.id)
|
||||
})
|
||||
|
||||
it('should allow deep virtual field as reference to ID', async () => {
|
||||
const category = await payload.create({
|
||||
collection: 'categories',
|
||||
data: { title: 'category-3' },
|
||||
})
|
||||
const post = await payload.create({
|
||||
collection: 'posts',
|
||||
data: { category: category.id, title: 'my-title-3' },
|
||||
})
|
||||
const { id } = await payload.create({
|
||||
collection: 'virtual-relations',
|
||||
depth: 0,
|
||||
data: { post: post.id },
|
||||
})
|
||||
|
||||
const docDepth2 = await payload.findByID({ collection: 'virtual-relations', id })
|
||||
expect(docDepth2.postCategoryID).toBe(category.id)
|
||||
const docDepth0 = await payload.findByID({ collection: 'virtual-relations', id, depth: 0 })
|
||||
expect(docDepth0.postCategoryID).toBe(category.id)
|
||||
})
|
||||
|
||||
it('should allow virtual field with reference localized', async () => {
|
||||
const post = await payload.create({
|
||||
collection: 'posts',
|
||||
@@ -2351,4 +2403,15 @@ describe('database', () => {
|
||||
|
||||
payload.db.allowAdditionalKeys = false
|
||||
})
|
||||
|
||||
it('should not crash when the version field is not selected', async () => {
|
||||
const customID = await payload.create({ collection: 'custom-ids', data: {} })
|
||||
const res = await payload.db.queryDrafts({
|
||||
collection: 'custom-ids',
|
||||
where: { parent: { equals: customID.id } },
|
||||
select: { parent: true },
|
||||
})
|
||||
|
||||
expect(res.docs[0].id).toBe(customID.id)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -373,8 +373,39 @@ export interface VirtualRelation {
|
||||
id: string;
|
||||
postTitle?: string | null;
|
||||
postCategoryTitle?: string | null;
|
||||
postCategoryID?:
|
||||
| {
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| unknown[]
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null;
|
||||
postID?:
|
||||
| {
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| unknown[]
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null;
|
||||
postLocalized?: string | null;
|
||||
post?: (string | null) | Post;
|
||||
customID?: (string | null) | CustomId;
|
||||
customIDValue?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: ('draft' | 'published') | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "custom-ids".
|
||||
*/
|
||||
export interface CustomId {
|
||||
id: string;
|
||||
title?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: ('draft' | 'published') | null;
|
||||
@@ -398,17 +429,6 @@ export interface FieldsPersistance {
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "custom-ids".
|
||||
*/
|
||||
export interface CustomId {
|
||||
id: string;
|
||||
title?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: ('draft' | 'published') | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "fake-custom-ids".
|
||||
@@ -807,8 +827,12 @@ export interface PlacesSelect<T extends boolean = true> {
|
||||
export interface VirtualRelationsSelect<T extends boolean = true> {
|
||||
postTitle?: T;
|
||||
postCategoryTitle?: T;
|
||||
postCategoryID?: T;
|
||||
postID?: T;
|
||||
postLocalized?: T;
|
||||
post?: T;
|
||||
customID?: T;
|
||||
customIDValue?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
_status?: T;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Payload } from 'payload'
|
||||
|
||||
import path from 'path'
|
||||
import { NotFound, type Payload } from 'payload'
|
||||
import { wait } from 'payload/shared'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
@@ -300,8 +301,8 @@ describe('@payloadcms/plugin-search', () => {
|
||||
collection: 'search',
|
||||
depth: 0,
|
||||
where: {
|
||||
'doc.value': {
|
||||
equals: page.id,
|
||||
id: {
|
||||
equals: results[0].id,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -82,19 +82,15 @@ describe('Sort functionality', () => {
|
||||
await page.getByText('Join A').click()
|
||||
await expect(page.locator('.sort-header button')).toHaveCount(2)
|
||||
|
||||
await page.locator('.sort-header button').nth(0).click()
|
||||
await assertRows(0, 'A', 'B', 'C', 'D')
|
||||
await moveRow(2, 3, 'success', 0) // move to middle
|
||||
await assertRows(0, 'A', 'C', 'B', 'D')
|
||||
|
||||
await page.locator('.sort-header button').nth(1).click()
|
||||
await assertRows(1, 'A', 'B', 'C', 'D')
|
||||
await moveRow(1, 4, 'success', 1) // move to end
|
||||
await assertRows(1, 'B', 'C', 'D', 'A')
|
||||
|
||||
await page.reload()
|
||||
await page.locator('.sort-header button').nth(0).click()
|
||||
await page.locator('.sort-header button').nth(1).click()
|
||||
await assertRows(0, 'A', 'C', 'B', 'D')
|
||||
await assertRows(1, 'B', 'C', 'D', 'A')
|
||||
})
|
||||
|
||||
32
test/versions/collections/AutosaveWithDraftButton.ts
Normal file
32
test/versions/collections/AutosaveWithDraftButton.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
import { autosaveWithDraftButtonSlug } from '../slugs.js'
|
||||
|
||||
const AutosaveWithDraftButtonPosts: CollectionConfig = {
|
||||
slug: autosaveWithDraftButtonSlug,
|
||||
labels: {
|
||||
singular: 'Autosave with Draft Button Post',
|
||||
plural: 'Autosave with Draft Button Posts',
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
defaultColumns: ['title', 'subtitle', 'createdAt', '_status'],
|
||||
},
|
||||
versions: {
|
||||
drafts: {
|
||||
autosave: {
|
||||
showSaveDraftButton: true,
|
||||
interval: 1000,
|
||||
},
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default AutosaveWithDraftButtonPosts
|
||||
@@ -4,6 +4,7 @@ const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
|
||||
import AutosavePosts from './collections/Autosave.js'
|
||||
import AutosaveWithDraftButtonPosts from './collections/AutosaveWithDraftButton.js'
|
||||
import AutosaveWithValidate from './collections/AutosaveWithValidate.js'
|
||||
import CustomIDs from './collections/CustomIDs.js'
|
||||
import { Diff } from './collections/Diff/index.js'
|
||||
@@ -17,6 +18,7 @@ import Posts from './collections/Posts.js'
|
||||
import { TextCollection } from './collections/Text.js'
|
||||
import VersionPosts from './collections/Versions.js'
|
||||
import AutosaveGlobal from './globals/Autosave.js'
|
||||
import AutosaveWithDraftButtonGlobal from './globals/AutosaveWithDraftButton.js'
|
||||
import DisablePublishGlobal from './globals/DisablePublish.js'
|
||||
import DraftGlobal from './globals/Draft.js'
|
||||
import DraftWithMaxGlobal from './globals/DraftWithMax.js'
|
||||
@@ -35,6 +37,7 @@ export default buildConfigWithDefaults({
|
||||
DisablePublish,
|
||||
Posts,
|
||||
AutosavePosts,
|
||||
AutosaveWithDraftButtonPosts,
|
||||
AutosaveWithValidate,
|
||||
DraftPosts,
|
||||
DraftWithMax,
|
||||
@@ -46,7 +49,14 @@ export default buildConfigWithDefaults({
|
||||
TextCollection,
|
||||
Media,
|
||||
],
|
||||
globals: [AutosaveGlobal, DraftGlobal, DraftWithMaxGlobal, DisablePublishGlobal, LocalizedGlobal],
|
||||
globals: [
|
||||
AutosaveGlobal,
|
||||
AutosaveWithDraftButtonGlobal,
|
||||
DraftGlobal,
|
||||
DraftWithMaxGlobal,
|
||||
DisablePublishGlobal,
|
||||
LocalizedGlobal,
|
||||
],
|
||||
indexSortableFields: true,
|
||||
localization: {
|
||||
defaultLocale: 'en',
|
||||
|
||||
@@ -48,6 +48,8 @@ import { POLL_TOPASS_TIMEOUT, TEST_TIMEOUT_LONG } from '../playwright.config.js'
|
||||
import {
|
||||
autosaveCollectionSlug,
|
||||
autoSaveGlobalSlug,
|
||||
autosaveWithDraftButtonGlobal,
|
||||
autosaveWithDraftButtonSlug,
|
||||
autosaveWithValidateCollectionSlug,
|
||||
customIDSlug,
|
||||
diffCollectionSlug,
|
||||
@@ -78,6 +80,7 @@ describe('Versions', () => {
|
||||
let url: AdminUrlUtil
|
||||
let serverURL: string
|
||||
let autosaveURL: AdminUrlUtil
|
||||
let autosaveWithDraftButtonURL: AdminUrlUtil
|
||||
let autosaveWithValidateURL: AdminUrlUtil
|
||||
let draftWithValidateURL: AdminUrlUtil
|
||||
let disablePublishURL: AdminUrlUtil
|
||||
@@ -116,6 +119,7 @@ describe('Versions', () => {
|
||||
beforeAll(() => {
|
||||
url = new AdminUrlUtil(serverURL, draftCollectionSlug)
|
||||
autosaveURL = new AdminUrlUtil(serverURL, autosaveCollectionSlug)
|
||||
autosaveWithDraftButtonURL = new AdminUrlUtil(serverURL, autosaveWithDraftButtonSlug)
|
||||
autosaveWithValidateURL = new AdminUrlUtil(serverURL, autosaveWithValidateCollectionSlug)
|
||||
disablePublishURL = new AdminUrlUtil(serverURL, disablePublishSlug)
|
||||
customIDURL = new AdminUrlUtil(serverURL, customIDSlug)
|
||||
@@ -201,78 +205,6 @@ describe('Versions', () => {
|
||||
await expect(page.locator('#field-title')).toHaveValue('v1')
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
// TODO: Check versions/:version-id view for collections / globals
|
||||
|
||||
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 () => {
|
||||
await page.goto(autosaveURL.create)
|
||||
await page.locator('#field-title').fill('autosave title')
|
||||
@@ -309,6 +241,16 @@ describe('Versions', () => {
|
||||
await expect(drawer.locator('.id-label')).toBeVisible()
|
||||
})
|
||||
|
||||
test('collection - should show "save as draft" button when showSaveDraftButton is true', async () => {
|
||||
await page.goto(autosaveWithDraftButtonURL.create)
|
||||
await expect(page.locator('#action-save-draft')).toBeVisible()
|
||||
})
|
||||
|
||||
test('collection - should not show "save as draft" button when showSaveDraftButton is false', async () => {
|
||||
await page.goto(autosaveURL.create)
|
||||
await expect(page.locator('#action-save-draft')).toBeHidden()
|
||||
})
|
||||
|
||||
test('collection - autosave - should not create duplicates when clicking Create new', async () => {
|
||||
// This test checks that when we click "Create new" in the list view, it only creates 1 extra document and not more
|
||||
const { totalDocs: initialDocsCount } = await payload.find({
|
||||
@@ -402,17 +344,6 @@ describe('Versions', () => {
|
||||
await expect(newUpdatedAt).not.toHaveText(initialUpdatedAt)
|
||||
})
|
||||
|
||||
test('global - should autosave', async () => {
|
||||
const url = new AdminUrlUtil(serverURL, autoSaveGlobalSlug)
|
||||
await page.goto(url.global(autoSaveGlobalSlug))
|
||||
const titleField = page.locator('#field-title')
|
||||
await titleField.fill('global title')
|
||||
await waitForAutoSaveToRunAndComplete(page)
|
||||
await expect(titleField).toHaveValue('global title')
|
||||
await page.goto(url.global(autoSaveGlobalSlug))
|
||||
await expect(page.locator('#field-title')).toHaveValue('global title')
|
||||
})
|
||||
|
||||
test('should retain localized data during autosave', async () => {
|
||||
const en = 'en'
|
||||
const es = 'es'
|
||||
@@ -519,12 +450,6 @@ describe('Versions', () => {
|
||||
await expect(page.locator('#field-title')).toHaveValue('title')
|
||||
})
|
||||
|
||||
test('globals — should hide publish button when access control prevents update', async () => {
|
||||
const url = new AdminUrlUtil(serverURL, disablePublishGlobalSlug)
|
||||
await page.goto(url.global(disablePublishGlobalSlug))
|
||||
await expect(page.locator('#action-save')).not.toBeAttached()
|
||||
})
|
||||
|
||||
test('collections — should hide publish button when access control prevents create', async () => {
|
||||
await page.goto(disablePublishURL.create)
|
||||
await expect(page.locator('#action-save')).not.toBeAttached()
|
||||
@@ -652,6 +577,107 @@ describe('Versions', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('draft globals', () => {
|
||||
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('global - should show "save as draft" button when showSaveDraftButton is true', async () => {
|
||||
const url = new AdminUrlUtil(serverURL, autosaveWithDraftButtonGlobal)
|
||||
await page.goto(url.global(autosaveWithDraftButtonGlobal))
|
||||
await expect(page.locator('#action-save-draft')).toBeVisible()
|
||||
})
|
||||
|
||||
test('global - should not show "save as draft" button when showSaveDraftButton is false', async () => {
|
||||
const url = new AdminUrlUtil(serverURL, autoSaveGlobalSlug)
|
||||
await page.goto(url.global(autoSaveGlobalSlug))
|
||||
await expect(page.locator('#action-save-draft')).toBeHidden()
|
||||
})
|
||||
|
||||
test('global - should autosave', async () => {
|
||||
const url = new AdminUrlUtil(serverURL, autoSaveGlobalSlug)
|
||||
await page.goto(url.global(autoSaveGlobalSlug))
|
||||
const titleField = page.locator('#field-title')
|
||||
await titleField.fill('global title')
|
||||
await waitForAutoSaveToRunAndComplete(page)
|
||||
await expect(titleField).toHaveValue('global title')
|
||||
await page.goto(url.global(autoSaveGlobalSlug))
|
||||
await expect(page.locator('#field-title')).toHaveValue('global title')
|
||||
})
|
||||
|
||||
test('globals — should hide publish button when access control prevents update', async () => {
|
||||
const url = new AdminUrlUtil(serverURL, disablePublishGlobalSlug)
|
||||
await page.goto(url.global(disablePublishGlobalSlug))
|
||||
await expect(page.locator('#action-save')).not.toBeAttached()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Scheduled publish', () => {
|
||||
beforeAll(() => {
|
||||
url = new AdminUrlUtil(serverURL, draftCollectionSlug)
|
||||
|
||||
26
test/versions/globals/AutosaveWithDraftButton.ts
Normal file
26
test/versions/globals/AutosaveWithDraftButton.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { GlobalConfig } from 'payload'
|
||||
|
||||
import { autosaveWithDraftButtonGlobal } from '../slugs.js'
|
||||
|
||||
const AutosaveWithDraftButtonGlobal: GlobalConfig = {
|
||||
slug: autosaveWithDraftButtonGlobal,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
label: 'Autosave with Draft Button Global',
|
||||
versions: {
|
||||
drafts: {
|
||||
autosave: {
|
||||
showSaveDraftButton: true,
|
||||
interval: 1000,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default AutosaveWithDraftButtonGlobal
|
||||
@@ -70,6 +70,7 @@ export interface Config {
|
||||
'disable-publish': DisablePublish;
|
||||
posts: Post;
|
||||
'autosave-posts': AutosavePost;
|
||||
'autosave-with-draft-button-posts': AutosaveWithDraftButtonPost;
|
||||
'autosave-with-validate-posts': AutosaveWithValidatePost;
|
||||
'draft-posts': DraftPost;
|
||||
'draft-with-max-posts': DraftWithMaxPost;
|
||||
@@ -91,6 +92,7 @@ export interface Config {
|
||||
'disable-publish': DisablePublishSelect<false> | DisablePublishSelect<true>;
|
||||
posts: PostsSelect<false> | PostsSelect<true>;
|
||||
'autosave-posts': AutosavePostsSelect<false> | AutosavePostsSelect<true>;
|
||||
'autosave-with-draft-button-posts': AutosaveWithDraftButtonPostsSelect<false> | AutosaveWithDraftButtonPostsSelect<true>;
|
||||
'autosave-with-validate-posts': AutosaveWithValidatePostsSelect<false> | AutosaveWithValidatePostsSelect<true>;
|
||||
'draft-posts': DraftPostsSelect<false> | DraftPostsSelect<true>;
|
||||
'draft-with-max-posts': DraftWithMaxPostsSelect<false> | DraftWithMaxPostsSelect<true>;
|
||||
@@ -112,6 +114,7 @@ export interface Config {
|
||||
};
|
||||
globals: {
|
||||
'autosave-global': AutosaveGlobal;
|
||||
'autosave-with-draft-button-global': AutosaveWithDraftButtonGlobal;
|
||||
'draft-global': DraftGlobal;
|
||||
'draft-with-max-global': DraftWithMaxGlobal;
|
||||
'disable-publish-global': DisablePublishGlobal;
|
||||
@@ -119,6 +122,7 @@ export interface Config {
|
||||
};
|
||||
globalsSelect: {
|
||||
'autosave-global': AutosaveGlobalSelect<false> | AutosaveGlobalSelect<true>;
|
||||
'autosave-with-draft-button-global': AutosaveWithDraftButtonGlobalSelect<false> | AutosaveWithDraftButtonGlobalSelect<true>;
|
||||
'draft-global': DraftGlobalSelect<false> | DraftGlobalSelect<true>;
|
||||
'draft-with-max-global': DraftWithMaxGlobalSelect<false> | DraftWithMaxGlobalSelect<true>;
|
||||
'disable-publish-global': DisablePublishGlobalSelect<false> | DisablePublishGlobalSelect<true>;
|
||||
@@ -228,6 +232,17 @@ export interface DraftPost {
|
||||
createdAt: string;
|
||||
_status?: ('draft' | 'published') | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "autosave-with-draft-button-posts".
|
||||
*/
|
||||
export interface AutosaveWithDraftButtonPost {
|
||||
id: string;
|
||||
title: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: ('draft' | 'published') | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "autosave-with-validate-posts".
|
||||
@@ -554,6 +569,10 @@ export interface PayloadLockedDocument {
|
||||
relationTo: 'autosave-posts';
|
||||
value: string | AutosavePost;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'autosave-with-draft-button-posts';
|
||||
value: string | AutosaveWithDraftButtonPost;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'autosave-with-validate-posts';
|
||||
value: string | AutosaveWithValidatePost;
|
||||
@@ -676,6 +695,16 @@ export interface AutosavePostsSelect<T extends boolean = true> {
|
||||
createdAt?: T;
|
||||
_status?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "autosave-with-draft-button-posts_select".
|
||||
*/
|
||||
export interface AutosaveWithDraftButtonPostsSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
_status?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "autosave-with-validate-posts_select".
|
||||
@@ -973,6 +1002,17 @@ export interface AutosaveGlobal {
|
||||
updatedAt?: string | null;
|
||||
createdAt?: string | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "autosave-with-draft-button-global".
|
||||
*/
|
||||
export interface AutosaveWithDraftButtonGlobal {
|
||||
id: string;
|
||||
title: string;
|
||||
_status?: ('draft' | 'published') | null;
|
||||
updatedAt?: string | null;
|
||||
createdAt?: string | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "draft-global".
|
||||
@@ -1029,6 +1069,17 @@ export interface AutosaveGlobalSelect<T extends boolean = true> {
|
||||
createdAt?: T;
|
||||
globalType?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "autosave-with-draft-button-global_select".
|
||||
*/
|
||||
export interface AutosaveWithDraftButtonGlobalSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
_status?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
globalType?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "draft-global_select".
|
||||
@@ -1082,10 +1133,15 @@ export interface TaskSchedulePublish {
|
||||
input: {
|
||||
type?: ('publish' | 'unpublish') | null;
|
||||
locale?: string | null;
|
||||
doc?: {
|
||||
relationTo: 'draft-posts';
|
||||
value: string | DraftPost;
|
||||
} | null;
|
||||
doc?:
|
||||
| ({
|
||||
relationTo: 'autosave-posts';
|
||||
value: string | AutosavePost;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'draft-posts';
|
||||
value: string | DraftPost;
|
||||
} | null);
|
||||
global?: 'draft-global' | null;
|
||||
user?: (string | null) | User;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export const autosaveCollectionSlug = 'autosave-posts'
|
||||
|
||||
export const autosaveWithDraftButtonSlug = 'autosave-with-draft-button-posts'
|
||||
|
||||
export const autosaveWithValidateCollectionSlug = 'autosave-with-validate-posts'
|
||||
|
||||
export const customIDSlug = 'custom-ids'
|
||||
@@ -33,7 +35,11 @@ export const collectionSlugs = [
|
||||
]
|
||||
|
||||
export const autoSaveGlobalSlug = 'autosave-global'
|
||||
|
||||
export const autosaveWithDraftButtonGlobal = 'autosave-with-draft-button-global'
|
||||
|
||||
export const draftGlobalSlug = 'draft-global'
|
||||
|
||||
export const draftWithMaxGlobalSlug = 'draft-with-max-global'
|
||||
|
||||
export const globalSlugs = [autoSaveGlobalSlug, draftGlobalSlug]
|
||||
|
||||
Reference in New Issue
Block a user