Compare commits
4 Commits
v3.0.0-bet
...
chore/beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8d01d64a5 | ||
|
|
11c3a65e63 | ||
|
|
8dd5e4dc24 | ||
|
|
9bd9e7a986 |
@@ -158,7 +158,7 @@
|
||||
"react-dom": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.20.2",
|
||||
"node": "^18.20.2 || >=20.9.0",
|
||||
"pnpm": "^8.15.7"
|
||||
},
|
||||
"pnpm": {
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.20.2"
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.20.2"
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
GraphQLUnionType,
|
||||
} from 'graphql'
|
||||
import { DateTimeResolver, EmailAddressResolver } from 'graphql-scalars'
|
||||
import { MissingEditorProp } from 'payload/errors'
|
||||
import { tabHasName } from 'payload/types'
|
||||
import { createDataloaderCacheKey, toWords } from 'payload/utilities'
|
||||
|
||||
@@ -476,6 +477,10 @@ function buildObjectType({
|
||||
async resolve(parent, args, context: Context) {
|
||||
let depth = config.defaultDepth
|
||||
if (typeof args.depth !== 'undefined') depth = args.depth
|
||||
if (!field?.editor) {
|
||||
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
|
||||
}
|
||||
|
||||
if (typeof field?.editor === 'function') {
|
||||
throw new Error('Attempted to access unsanitized rich text editor.')
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.20.2"
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.20.2 || >=20.6.0"
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
|
||||
@@ -101,7 +101,7 @@ export default joi.object({
|
||||
defaultMaxTextLength: joi.number(),
|
||||
editor: joi
|
||||
.object()
|
||||
.required()
|
||||
.optional()
|
||||
.keys({
|
||||
CellComponent: componentSchema.optional(),
|
||||
FieldComponent: componentSchema.optional(),
|
||||
|
||||
@@ -615,7 +615,7 @@ export type Config = {
|
||||
*/
|
||||
defaultMaxTextLength?: number
|
||||
/** Default richtext editor to use for richText fields */
|
||||
editor: RichTextAdapterProvider<any, any, any>
|
||||
editor?: RichTextAdapterProvider<any, any, any>
|
||||
/**
|
||||
* Email Adapter
|
||||
*
|
||||
@@ -747,7 +747,7 @@ export type SanitizedConfig = Omit<
|
||||
> & {
|
||||
collections: SanitizedCollectionConfig[]
|
||||
/** Default richtext editor to use for richText fields */
|
||||
editor: RichTextAdapter<any, any, any>
|
||||
editor?: RichTextAdapter<any, any, any>
|
||||
endpoints: Endpoint[]
|
||||
globals: SanitizedGlobalConfig[]
|
||||
i18n: Required<I18nOptions>
|
||||
|
||||
@@ -12,6 +12,7 @@ export { InvalidFieldName } from './InvalidFieldName.js'
|
||||
export { InvalidFieldRelationship } from './InvalidFieldRelationship.js'
|
||||
export { LockedAuth } from './LockedAuth.js'
|
||||
export { MissingCollectionLabel } from './MissingCollectionLabel.js'
|
||||
export { MissingEditorProp } from './MissingEditorProp.js'
|
||||
export { MissingFieldInputOptions } from './MissingFieldInputOptions.js'
|
||||
export { MissingFieldType } from './MissingFieldType.js'
|
||||
export { MissingFile } from './MissingFile.js'
|
||||
|
||||
@@ -2,8 +2,10 @@ export {
|
||||
APIError,
|
||||
AuthenticationError,
|
||||
DuplicateCollection,
|
||||
DuplicateFieldName,
|
||||
DuplicateGlobal,
|
||||
ErrorDeletingFile,
|
||||
FileRetrievalError,
|
||||
FileUploadError,
|
||||
Forbidden,
|
||||
InvalidConfiguration,
|
||||
@@ -11,6 +13,7 @@ export {
|
||||
InvalidFieldRelationship,
|
||||
LockedAuth,
|
||||
MissingCollectionLabel,
|
||||
MissingEditorProp,
|
||||
MissingFieldInputOptions,
|
||||
MissingFieldType,
|
||||
MissingFile,
|
||||
|
||||
@@ -158,7 +158,7 @@ export const sanitizeFields = async ({
|
||||
// config.editor should be sanitized at this point
|
||||
field.editor = _config.editor
|
||||
} else {
|
||||
throw new MissingEditorProp(field)
|
||||
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { SanitizedGlobalConfig } from '../../../globals/config/types.js'
|
||||
import type { PayloadRequestWithData, RequestContext } from '../../../types/index.js'
|
||||
import type { Field, TabAsField } from '../../config/types.js'
|
||||
|
||||
import { MissingEditorProp } from '../../../errors/index.js'
|
||||
import { fieldAffectsData, tabHasName } from '../../config/types.js'
|
||||
import getValueWithDefault from '../../getDefaultValue.js'
|
||||
import { relationshipPopulationPromise } from './relationshipPopulationPromise.js'
|
||||
@@ -143,6 +144,9 @@ export const promise = async ({
|
||||
}
|
||||
|
||||
case 'richText': {
|
||||
if (!field?.editor) {
|
||||
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
|
||||
}
|
||||
if (typeof field?.editor === 'function') {
|
||||
throw new Error('Attempted to access unsanitized rich text editor.')
|
||||
}
|
||||
|
||||
@@ -268,6 +268,9 @@ export const richText: Validate<object, unknown, unknown, RichTextField> = async
|
||||
value,
|
||||
options,
|
||||
) => {
|
||||
if (!options?.editor) {
|
||||
throw new Error('richText field has no editor property.')
|
||||
}
|
||||
if (typeof options?.editor === 'function') {
|
||||
throw new Error('Attempted to access unsanitized rich text editor.')
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import type { SanitizedConfig } from '../config/types.js'
|
||||
import type { Field, FieldAffectingData, Option } from '../fields/config/types.js'
|
||||
import type { SanitizedGlobalConfig } from '../globals/config/types.js'
|
||||
|
||||
import { MissingEditorProp } from '../errors/MissingEditorProp.js'
|
||||
import { fieldAffectsData, tabHasName } from '../fields/config/types.js'
|
||||
import { deepCopyObject } from './deepCopyObject.js'
|
||||
import { toWords } from './formatLabels.js'
|
||||
@@ -195,6 +196,9 @@ export function fieldsToJSONSchema(
|
||||
}
|
||||
|
||||
case 'richText': {
|
||||
if (!field?.editor) {
|
||||
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
|
||||
}
|
||||
if (typeof field.editor === 'function') {
|
||||
throw new Error('Attempted to access unsanitized rich text editor.')
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ export const enforceMaxVersions = async ({
|
||||
}
|
||||
|
||||
await payload.db.deleteVersions({
|
||||
collection: collection?.slug,
|
||||
collection: slug,
|
||||
req,
|
||||
where: deleteQuery,
|
||||
})
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
"react-dom": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.20.2"
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
"react": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.20.2"
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.20.2"
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.20.2"
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.20.2"
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.20.2"
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.20.2"
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
"react-dom": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.20.2"
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
|
||||
@@ -10,6 +10,7 @@ import type {
|
||||
SanitizedConfig,
|
||||
} from 'payload/types'
|
||||
|
||||
import { MissingEditorProp } from 'payload/errors'
|
||||
import { fieldAffectsData, fieldIsPresentationalOnly } from 'payload/types'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
@@ -566,6 +567,9 @@ export const mapFields = (args: {
|
||||
style: field.admin?.style,
|
||||
width: field.admin?.width,
|
||||
}
|
||||
if (!field?.editor) {
|
||||
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
|
||||
}
|
||||
if (typeof field?.editor === 'function') {
|
||||
throw new Error('Attempted to access unsanitized rich text editor.')
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { I18n } from '@payloadcms/translations'
|
||||
import type { Field, SanitizedConfig } from 'payload/types'
|
||||
|
||||
import { MissingEditorProp } from 'payload/errors'
|
||||
import { tabHasName } from 'payload/types'
|
||||
|
||||
import type { FieldSchemaMap } from './types.js'
|
||||
@@ -68,6 +69,9 @@ export const traverseFields = ({
|
||||
break
|
||||
|
||||
case 'richText':
|
||||
if (!field?.editor) {
|
||||
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
|
||||
}
|
||||
if (typeof field.editor === 'function') {
|
||||
throw new Error('Attempted to access unsanitized rich text editor.')
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.20.2 || >=20.6.0"
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.20.2 || >=20.6.0"
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"ci": "payload migrate && pnpm build",
|
||||
"dev": "next dev",
|
||||
"generate:types": "payload generate:types",
|
||||
"lint": "next lint",
|
||||
"payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
|
||||
"generate:types": "payload generate:types"
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/db-postgres": "beta",
|
||||
@@ -34,5 +34,8 @@
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.3.0",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,9 @@
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"overrides": {
|
||||
"@types/react": "18.2.74"
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.20.2 || >=20.6.0"
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
"@payloadcms/next": "beta",
|
||||
"@payloadcms/plugin-cloud": "beta",
|
||||
"@payloadcms/richtext-lexical": "beta",
|
||||
"@payloadcms/storage-vercel-blob": "beta",
|
||||
"cross-env": "^7.0.3",
|
||||
"graphql": "^16.8.1",
|
||||
"next": "15.0.0-rc.0",
|
||||
"payload": "beta",
|
||||
"react": "^19.0.0-rc-f994737d14-20240522",
|
||||
"react-dom": "^19.0.0-rc-f994737d14-20240522",
|
||||
"@payloadcms/storage-vercel-blob": "beta"
|
||||
"react-dom": "^19.0.0-rc-f994737d14-20240522"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.12.12",
|
||||
@@ -36,7 +36,7 @@
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.20.2 || >=20.6.0"
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
|
||||
@@ -14,17 +14,17 @@
|
||||
"start": "cross-env NODE_OPTIONS=--no-deprecation next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/db-postgres": "beta",
|
||||
"@payloadcms/next": "beta",
|
||||
"@payloadcms/plugin-cloud": "beta",
|
||||
"@payloadcms/richtext-lexical": "beta",
|
||||
"@payloadcms/storage-vercel-blob": "beta",
|
||||
"cross-env": "^7.0.3",
|
||||
"graphql": "^16.8.1",
|
||||
"next": "15.0.0-rc.0",
|
||||
"payload": "beta",
|
||||
"react": "^19.0.0-rc-f994737d14-20240522",
|
||||
"react-dom": "^19.0.0-rc-f994737d14-20240522",
|
||||
"@payloadcms/db-postgres": "beta",
|
||||
"@payloadcms/storage-vercel-blob": "beta"
|
||||
"react-dom": "^19.0.0-rc-f994737d14-20240522"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.12.12",
|
||||
@@ -36,7 +36,7 @@
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.20.2 || >=20.6.0"
|
||||
"node": "^18.20.2 || >=20.9.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
|
||||
@@ -11,8 +11,10 @@ import { Uploads1 } from './collections/Upload1/index.js'
|
||||
import { Uploads2 } from './collections/Upload2/index.js'
|
||||
import {
|
||||
audioSlug,
|
||||
cropOnlySlug,
|
||||
enlargeSlug,
|
||||
focalNoSizesSlug,
|
||||
globalWithMediaSlug,
|
||||
mediaSlug,
|
||||
reduceSlug,
|
||||
relationSlug,
|
||||
@@ -132,7 +134,7 @@ export default buildConfigWithDefaults({
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: 'crop-only',
|
||||
slug: cropOnlySlug,
|
||||
fields: [],
|
||||
upload: {
|
||||
focalPoint: false,
|
||||
@@ -462,6 +464,18 @@ export default buildConfigWithDefaults({
|
||||
},
|
||||
},
|
||||
],
|
||||
globals: [
|
||||
{
|
||||
slug: globalWithMediaSlug,
|
||||
fields: [
|
||||
{
|
||||
type: 'upload',
|
||||
name: 'media',
|
||||
relationTo: cropOnlySlug,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
onInit: async (payload) => {
|
||||
const uploadsDir = path.resolve(dirname, './media')
|
||||
removeFiles(path.normalize(uploadsDir))
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { Page } from '@playwright/test'
|
||||
import type { Payload } from 'payload/types'
|
||||
|
||||
import { expect, test } from '@playwright/test'
|
||||
import path from 'path'
|
||||
@@ -23,6 +22,7 @@ import {
|
||||
adminThumbnailFunctionSlug,
|
||||
adminThumbnailSizeSlug,
|
||||
audioSlug,
|
||||
globalWithMediaSlug,
|
||||
mediaSlug,
|
||||
relationSlug,
|
||||
} from './shared.js'
|
||||
@@ -39,6 +39,7 @@ let audioURL: AdminUrlUtil
|
||||
let relationURL: AdminUrlUtil
|
||||
let adminThumbnailSizeURL: AdminUrlUtil
|
||||
let adminThumbnailFunctionURL: AdminUrlUtil
|
||||
let globalWithMediaURL: string
|
||||
|
||||
describe('uploads', () => {
|
||||
let page: Page
|
||||
@@ -56,6 +57,7 @@ describe('uploads', () => {
|
||||
relationURL = new AdminUrlUtil(serverURL, relationSlug)
|
||||
adminThumbnailSizeURL = new AdminUrlUtil(serverURL, adminThumbnailSizeSlug)
|
||||
adminThumbnailFunctionURL = new AdminUrlUtil(serverURL, adminThumbnailFunctionSlug)
|
||||
globalWithMediaURL = new AdminUrlUtil(serverURL, globalWithMediaURL).global(globalWithMediaSlug)
|
||||
|
||||
const context = await browser.newContext()
|
||||
page = await context.newPage()
|
||||
@@ -373,4 +375,17 @@ describe('uploads', () => {
|
||||
expect(redDoc.filesize).toEqual(1207)
|
||||
})
|
||||
})
|
||||
|
||||
describe('globals', () => {
|
||||
test('should be able to crop media from a global', async () => {
|
||||
await page.goto(globalWithMediaURL)
|
||||
await page.click('.upload__toggler.doc-drawer__toggler')
|
||||
await page.setInputFiles('input[type="file"]', path.resolve(dirname, './image.png'))
|
||||
await page.click('.file-field__edit')
|
||||
await page.click('.btn.edit-upload__save')
|
||||
await saveDocAndAssert(page, '.drawer__content #action-save')
|
||||
await saveDocAndAssert(page)
|
||||
await expect(page.locator('.thumbnail img')).toBeVisible()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,8 +5,10 @@ export const audioSlug = 'audio'
|
||||
export const enlargeSlug = 'enlarge'
|
||||
export const focalNoSizesSlug = 'focal-no-sizes'
|
||||
export const focalOnlySlug = 'focal-only'
|
||||
export const cropOnlySlug = 'crop-only'
|
||||
export const reduceSlug = 'reduce'
|
||||
export const adminThumbnailFunctionSlug = 'admin-thumbnail-function'
|
||||
export const adminThumbnailSizeSlug = 'admin-thumbnail-size'
|
||||
export const unstoredMediaSlug = 'unstored-media'
|
||||
export const versionSlug = 'versions'
|
||||
export const globalWithMediaSlug = 'globals-with-media'
|
||||
|
||||
125
test/versions/collections/DraftsWithMax.ts
Normal file
125
test/versions/collections/DraftsWithMax.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import CollectionVersionButton from '../elements/CollectionVersionButton/index.js'
|
||||
import CollectionVersionsButton from '../elements/CollectionVersionsButton/index.js'
|
||||
import { CustomPublishButton } from '../elements/CustomSaveButton/index.js'
|
||||
import { draftWithMaxCollectionSlug } from '../slugs.js'
|
||||
|
||||
const DraftWithMaxPosts: CollectionConfig = {
|
||||
slug: draftWithMaxCollectionSlug,
|
||||
access: {
|
||||
read: ({ req: { user } }) => {
|
||||
if (user) {
|
||||
return true
|
||||
}
|
||||
|
||||
return {
|
||||
or: [
|
||||
{
|
||||
_status: {
|
||||
equals: 'published',
|
||||
},
|
||||
},
|
||||
{
|
||||
_status: {
|
||||
exists: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
readVersions: ({ req: { user } }) => Boolean(user),
|
||||
},
|
||||
admin: {
|
||||
components: {
|
||||
edit: {
|
||||
PublishButton: CustomPublishButton,
|
||||
},
|
||||
views: {
|
||||
Edit: {
|
||||
Version: {
|
||||
actions: [CollectionVersionButton],
|
||||
},
|
||||
Versions: {
|
||||
actions: [CollectionVersionsButton],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultColumns: ['title', 'description', 'createdAt', '_status'],
|
||||
preview: () => 'https://payloadcms.com',
|
||||
useAsTitle: 'title',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
label: 'Title',
|
||||
localized: true,
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'textarea',
|
||||
label: 'Description',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'radio',
|
||||
type: 'radio',
|
||||
options: [
|
||||
{
|
||||
label: { en: 'Test en', es: 'Test es' },
|
||||
value: 'test',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'select',
|
||||
type: 'select',
|
||||
hasMany: true,
|
||||
options: [
|
||||
{
|
||||
label: { en: 'Test1 en', es: 'Test1 es' },
|
||||
value: 'test1',
|
||||
},
|
||||
{
|
||||
label: { en: 'Test2 en', es: 'Test2 es' },
|
||||
value: 'test2',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'blocksField',
|
||||
type: 'blocks',
|
||||
blocks: [
|
||||
{
|
||||
slug: 'block',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'localized',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'relation',
|
||||
type: 'relationship',
|
||||
relationTo: draftWithMaxCollectionSlug,
|
||||
},
|
||||
],
|
||||
versions: {
|
||||
drafts: true,
|
||||
maxPerDoc: 1,
|
||||
},
|
||||
}
|
||||
|
||||
export default DraftWithMaxPosts
|
||||
@@ -3,16 +3,26 @@ import AutosavePosts from './collections/Autosave.js'
|
||||
import CustomIDs from './collections/CustomIDs.js'
|
||||
import DisablePublish from './collections/DisablePublish.js'
|
||||
import DraftPosts from './collections/Drafts.js'
|
||||
import DraftWithMax from './collections/DraftsWithMax.js'
|
||||
import Posts from './collections/Posts.js'
|
||||
import VersionPosts from './collections/Versions.js'
|
||||
import AutosaveGlobal from './globals/Autosave.js'
|
||||
import DisablePublishGlobal from './globals/DisablePublish.js'
|
||||
import DraftGlobal from './globals/Draft.js'
|
||||
import DraftWithMaxGlobal from './globals/DraftWithMax.js'
|
||||
import { seed } from './seed.js'
|
||||
|
||||
export default buildConfigWithDefaults({
|
||||
collections: [DisablePublish, Posts, AutosavePosts, DraftPosts, VersionPosts, CustomIDs],
|
||||
globals: [AutosaveGlobal, DraftGlobal, DisablePublishGlobal],
|
||||
collections: [
|
||||
DisablePublish,
|
||||
Posts,
|
||||
AutosavePosts,
|
||||
DraftPosts,
|
||||
DraftWithMax,
|
||||
VersionPosts,
|
||||
CustomIDs,
|
||||
],
|
||||
globals: [AutosaveGlobal, DraftGlobal, DraftWithMaxGlobal, DisablePublishGlobal],
|
||||
indexSortableFields: true,
|
||||
localization: {
|
||||
defaultLocale: 'en',
|
||||
|
||||
@@ -55,6 +55,8 @@ import {
|
||||
disablePublishSlug,
|
||||
draftCollectionSlug,
|
||||
draftGlobalSlug,
|
||||
draftWithMaxCollectionSlug,
|
||||
draftWithMaxGlobalSlug,
|
||||
postCollectionSlug,
|
||||
} from './slugs.js'
|
||||
|
||||
@@ -352,6 +354,44 @@ describe('versions', () => {
|
||||
expect(href).toBe(`${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, globalSlug)
|
||||
const versionsURL = `${global.global(globalSlug)}/versions`
|
||||
@@ -535,5 +575,45 @@ describe('versions', () => {
|
||||
|
||||
await expect(page.locator('.rs__option')).toHaveText('some title')
|
||||
})
|
||||
|
||||
test('collection — respects max number of versions', async () => {
|
||||
const maxOneCollection = await payload.create({
|
||||
collection: draftWithMaxCollectionSlug,
|
||||
data: {
|
||||
title: 'initial title',
|
||||
description: 'some description',
|
||||
},
|
||||
draft: true,
|
||||
})
|
||||
|
||||
const collection = new AdminUrlUtil(serverURL, draftWithMaxCollectionSlug)
|
||||
await page.goto(collection.edit(maxOneCollection.id))
|
||||
|
||||
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()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
61
test/versions/globals/DraftWithMax.ts
Normal file
61
test/versions/globals/DraftWithMax.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import type { GlobalConfig } from 'payload/types'
|
||||
|
||||
import GlobalVersionButton from '../elements/GlobalVersionButton/index.js'
|
||||
import GlobalVersionsButton from '../elements/GlobalVersionsButton/index.js'
|
||||
import { draftWithMaxGlobalSlug } from '../slugs.js'
|
||||
|
||||
const DraftWithMaxGlobal: GlobalConfig = {
|
||||
slug: draftWithMaxGlobalSlug,
|
||||
label: 'Draft Global',
|
||||
admin: {
|
||||
preview: () => 'https://payloadcms.com',
|
||||
components: {
|
||||
views: {
|
||||
Edit: {
|
||||
Version: {
|
||||
actions: [GlobalVersionButton],
|
||||
},
|
||||
Versions: {
|
||||
actions: [GlobalVersionsButton],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
versions: {
|
||||
max: 1,
|
||||
drafts: true,
|
||||
},
|
||||
access: {
|
||||
read: ({ req: { user } }) => {
|
||||
if (user) {
|
||||
return true
|
||||
}
|
||||
|
||||
return {
|
||||
or: [
|
||||
{
|
||||
_status: {
|
||||
equals: 'published',
|
||||
},
|
||||
},
|
||||
{
|
||||
_status: {
|
||||
exists: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default DraftWithMaxGlobal
|
||||
@@ -12,6 +12,7 @@ export interface Config {
|
||||
posts: Post;
|
||||
'autosave-posts': AutosavePost;
|
||||
'draft-posts': DraftPost;
|
||||
'draft-with-max-posts': DraftWithMaxPost;
|
||||
'version-posts': VersionPost;
|
||||
'custom-ids': CustomId;
|
||||
users: User;
|
||||
@@ -21,6 +22,7 @@ export interface Config {
|
||||
globals: {
|
||||
'autosave-global': AutosaveGlobal;
|
||||
'draft-global': DraftGlobal;
|
||||
'draft-with-max-global': DraftWithMaxGlobal;
|
||||
'disable-publish-global': DisablePublishGlobal;
|
||||
};
|
||||
locale: 'en' | 'es';
|
||||
@@ -98,6 +100,30 @@ export interface DraftPost {
|
||||
createdAt: string;
|
||||
_status?: ('draft' | 'published') | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "draft-with-max-posts".
|
||||
*/
|
||||
export interface DraftWithMaxPost {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
radio?: 'test' | null;
|
||||
select?: ('test1' | 'test2')[] | null;
|
||||
blocksField?:
|
||||
| {
|
||||
text?: string | null;
|
||||
localized?: string | null;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'block';
|
||||
}[]
|
||||
| null;
|
||||
relation?: (string | null) | DraftWithMaxPost;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: ('draft' | 'published') | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "custom-ids".
|
||||
@@ -181,6 +207,17 @@ export interface DraftGlobal {
|
||||
updatedAt?: string | null;
|
||||
createdAt?: string | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "draft-with-max-global".
|
||||
*/
|
||||
export interface DraftWithMaxGlobal {
|
||||
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` "disable-publish-global".
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
export const autosaveCollectionSlug = 'autosave-posts' as const
|
||||
export const autosaveCollectionSlug = 'autosave-posts'
|
||||
|
||||
export const customIDSlug = 'custom-ids' as const
|
||||
export const customIDSlug = 'custom-ids'
|
||||
|
||||
export const draftCollectionSlug = 'draft-posts' as const
|
||||
export const draftCollectionSlug = 'draft-posts'
|
||||
export const draftWithMaxCollectionSlug = 'draft-with-max-posts'
|
||||
|
||||
export const postCollectionSlug = 'posts' as const
|
||||
export const postCollectionSlug = 'posts'
|
||||
|
||||
export const versionCollectionSlug = 'version-posts' as const
|
||||
export const versionCollectionSlug = 'version-posts'
|
||||
|
||||
export const disablePublishSlug = 'disable-publish' as const
|
||||
export const disablePublishSlug = 'disable-publish'
|
||||
|
||||
export const disablePublishGlobalSlug = 'disable-publish-global' as const
|
||||
export const disablePublishGlobalSlug = 'disable-publish-global'
|
||||
|
||||
export const collectionSlugs = [
|
||||
autosaveCollectionSlug,
|
||||
@@ -19,7 +20,8 @@ export const collectionSlugs = [
|
||||
versionCollectionSlug,
|
||||
]
|
||||
|
||||
export const autoSaveGlobalSlug = 'autosave-global' as const
|
||||
export const draftGlobalSlug = 'draft-global' as const
|
||||
export const autoSaveGlobalSlug = 'autosave-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