Compare commits

..

27 Commits

Author SHA1 Message Date
Elliot DeNolf
c84c58c7b4 chore(release): db-postgres/0.4.0 [skip ci] 2024-01-16 16:33:21 -05:00
Elliot DeNolf
1c1b8f3cec chore(release): db-mongodb/1.3.2 [skip ci] 2024-01-16 16:32:52 -05:00
Elliot DeNolf
3f69f83180 chore(release): payload/2.8.2 [skip ci] 2024-01-16 16:31:39 -05:00
beezee
371353f153 feat(db-postgres): support drizzle logging config (#4809) 2024-01-16 15:45:17 -05:00
Dan Ribbens
a92c6334b6 chore(db-postgres): update drizzle-kit to 0.20.5-608ae62 and drizzle-orm to latest (#4772) 2024-01-16 13:35:07 -05:00
Paul
eb9e771a9c fix(db-postgres): Remove duplicate keys from response (#4747)
* Remove duplicate keys from response

* Update to delete duplicates at a higher level and remove '_order' from array rows too
2024-01-16 13:22:08 -05:00
Patrik
ee5390aaca fix: removes max-width from field-types class & correctly sets it on uploads (#4829) 2024-01-16 13:12:58 -05:00
Paul
a861311c5a fix(db-mongodb): mongodb versions creating duplicates (#4825)
* Fixes the issue with mongodb versions

* Update other methods to use options too
2024-01-16 12:20:45 -05:00
James Mikrut
74c3fe1bb2 Merge pull request #4806 from payloadcms/fix/#4775-postgres-block-validation-arrays
fix(db-postgres): validateExistingBlockIsIdentical with arrays
2024-01-15 15:04:47 -05:00
James Mikrut
a2be50279e Merge pull request #4804 from payloadcms/fix/#4802-transaction-options-false
fix(db-mongodb): transactionOptions=false typeErrors
2024-01-15 15:04:01 -05:00
James Mikrut
403eb06acf Merge pull request #4723 from payloadcms/fix/4548-fix-missing-spread
fix(plugin-seo):Fix missing spread operator in URL generator function
2024-01-15 15:02:45 -05:00
James Mikrut
f5c2cd74cc Merge pull request #4695 from payloadcms/feat/4539-seo-plugin-allow-field-and-interface-overrides
feat(plugin-seo): Add support for interfaceName and fieldOverrides
2024-01-15 15:02:14 -05:00
Paul Popus
a6a1963ec6 Merge branch 'main' into feat/4539-seo-plugin-allow-field-and-interface-overrides 2024-01-15 16:29:49 -03:00
Dan Ribbens
0647c870f1 fix(db-postgres): validateExistingBlockIsIdentical with other tables 2024-01-13 22:46:05 -05:00
Dan Ribbens
3b88adc7d0 fix(db-postgres): validateExistingBlockIsIdentical with arrays 2024-01-13 22:40:30 -05:00
Dan Ribbens
82383a5b5f fix(db-mongodb): transactionOptions=false typeErrors 2024-01-13 14:59:16 -05:00
James Mikrut
f9dda628b2 Merge pull request #4730 from payloadcms/feat/4471-add-validation-for-form-submission
feat(plugin-form-builder):Add validation for form ID when creating a form submissions
2024-01-12 15:39:35 -05:00
Elliot DeNolf
93eb0e4a31 chore: update bug report template to renamed possible-bug label 2024-01-12 14:19:43 -05:00
Elliot DeNolf
2e362f44f4 chore(release): payload/2.8.1 [skip ci] 2024-01-12 12:44:15 -05:00
Jarrod Flesch
775502b161 fix: corrects config usage in build bin script (#4796) 2024-01-12 12:40:08 -05:00
Elliot DeNolf
84d75ce6ca chore(release): plugin-form-builder/1.1.2 [skip ci] 2024-01-12 10:47:08 -05:00
Elliot DeNolf
175cf229c0 chore(release): richtext-lexical/0.5.2 [skip ci] 2024-01-12 10:41:55 -05:00
Paul Popus
2b731c1088 feat(plugin-form-builder):Add validation for form ID when creating a submission 2024-01-08 20:35:52 -03:00
Paul Popus
6affa1c304 Removed fallback for interfaceName 2024-01-08 15:27:17 -03:00
Paul Popus
57dc93da5d Fix missing spread operator in generator function 2024-01-08 14:07:35 -03:00
Paul Popus
28d3f73c2a Fix integration test 2024-01-05 15:23:57 -03:00
Paul Popus
7eae86bcb3 Add changes from previous branch and update docs 2024-01-05 12:45:03 -03:00
46 changed files with 1012 additions and 189 deletions

View File

@@ -1,6 +1,6 @@
name: Bug Report name: Bug Report
description: Create a bug report for Payload description: Create a bug report for Payload
labels: ['possible-bug'] labels: ['[possible-bug]']
body: body:
- type: markdown - type: markdown
attributes: attributes:

View File

@@ -1,3 +1,28 @@
## [2.8.2](https://github.com/payloadcms/payload/compare/v2.8.1...v2.8.2) (2024-01-16)
### Features
* **db-postgres:** support drizzle logging config ([#4809](https://github.com/payloadcms/payload/issues/4809)) ([371353f](https://github.com/payloadcms/payload/commit/371353f1535fbab4ebd9f56fc14fd10a30eec289))
* **plugin-form-builder:** add validation for form ID when creating a submission
* **plugin-seo:** allow field and interface overrides
### Bug Fixes
* **db-mongodb:** mongodb versions creating duplicates ([#4825](https://github.com/payloadcms/payload/issues/4825)) ([a861311](https://github.com/payloadcms/payload/commit/a861311c5a98126700f98f9a2ab380782e754717))
* **db-mongodb:** transactionOptions=false typeErrors ([82383a5](https://github.com/payloadcms/payload/commit/82383a5b5f52785115c0feb970da70e91971b7ca))
* **db-postgres:** Remove duplicate keys from response ([#4747](https://github.com/payloadcms/payload/issues/4747)) ([eb9e771](https://github.com/payloadcms/payload/commit/eb9e771a9ca03636486d36654f215b73435574cb))
* **db-postgres:** validateExistingBlockIsIdentical with arrays ([3b88adc](https://github.com/payloadcms/payload/commit/3b88adc7d0594af63ce190c40c9ee3905df67a31))
* **db-postgres:** validateExistingBlockIsIdentical with other tables ([0647c87](https://github.com/payloadcms/payload/commit/0647c870f15dc1b122734b678c2abeb6f56377d4))
* removes max-width from field-types class & correctly sets it on uploads ([#4829](https://github.com/payloadcms/payload/issues/4829)) ([ee5390a](https://github.com/payloadcms/payload/commit/ee5390aaca37a4154cde8392b60f091ec3e5175c))
## [2.8.1](https://github.com/payloadcms/payload/compare/v2.8.0...v2.8.1) (2024-01-12)
### Bug Fixes
* corrects config usage in build bin script ([#4796](https://github.com/payloadcms/payload/issues/4796)) ([775502b](https://github.com/payloadcms/payload/commit/775502b1616c1bd35a3044438e253a0e84219f99))
## [2.8.0](https://github.com/payloadcms/payload/compare/v2.7.0...v2.8.0) (2024-01-12) ## [2.8.0](https://github.com/payloadcms/payload/compare/v2.7.0...v2.8.0) (2024-01-12)

View File

@@ -159,6 +159,39 @@ A function called by the search preview component to display the actual URL of y
} }
``` ```
#### `interfaceName`
Rename the meta group interface name that is generated for TypeScript and GraphQL.
```ts
// payload.config.ts
{
// ...
seoPlugin({
interfaceName: 'customInterfaceNameSEO'
})
}
```
#### `fieldOverrides`
Pass any valid field props to the base fields: Title, Description or Image.
```ts
// payload.config.ts
seoPlugin({
// ...
fieldOverrides: {
title: {
required: true,
},
description: {
localized: true,
},
},
})
```
## TypeScript ## TypeScript
All types can be directly imported: All types can be directly imported:

View File

@@ -1,6 +1,6 @@
{ {
"name": "@payloadcms/db-mongodb", "name": "@payloadcms/db-mongodb",
"version": "1.3.1", "version": "1.3.2",
"description": "The officially supported MongoDB database adapter for Payload", "description": "The officially supported MongoDB database adapter for Payload",
"repository": "https://github.com/payloadcms/payload", "repository": "https://github.com/payloadcms/payload",
"license": "MIT", "license": "MIT",

View File

@@ -50,7 +50,7 @@ export const connect: Connect = async function connect(this: MongooseAdapter, pa
const client = this.connection.getClient() const client = this.connection.getClient()
if (!client.options.replicaSet || this.transactionOptions === false) { if (!client.options.replicaSet) {
this.transactionOptions = false this.transactionOptions = false
this.beginTransaction = undefined this.beginTransaction = undefined
} }

View File

@@ -49,6 +49,7 @@ export const createGlobalVersion: CreateGlobalVersion = async function createGlo
], ],
}, },
{ $unset: { latest: 1 } }, { $unset: { latest: 1 } },
options,
) )
const result: Document = JSON.parse(JSON.stringify(doc)) const result: Document = JSON.parse(JSON.stringify(doc))

View File

@@ -57,6 +57,7 @@ export const createVersion: CreateVersion = async function createVersion(
], ],
}, },
{ $unset: { latest: 1 } }, { $unset: { latest: 1 } },
options,
) )
const result: Document = JSON.parse(JSON.stringify(doc)) const result: Document = JSON.parse(JSON.stringify(doc))

View File

@@ -63,6 +63,7 @@ export const find: Find = async function find(
paginationOptions.useCustomCountFn = () => { paginationOptions.useCustomCountFn = () => {
return Promise.resolve( return Promise.resolve(
Model.countDocuments(query, { Model.countDocuments(query, {
...options,
hint: { _id: 1 }, hint: { _id: 1 },
}), }),
) )

View File

@@ -82,6 +82,7 @@ export const findGlobalVersions: FindGlobalVersions = async function findGlobalV
paginationOptions.useCustomCountFn = () => { paginationOptions.useCustomCountFn = () => {
return Promise.resolve( return Promise.resolve(
Model.countDocuments(query, { Model.countDocuments(query, {
...options,
hint: { _id: 1 }, hint: { _id: 1 },
}), }),
) )

View File

@@ -79,6 +79,7 @@ export const findVersions: FindVersions = async function findVersions(
paginationOptions.useCustomCountFn = () => { paginationOptions.useCustomCountFn = () => {
return Promise.resolve( return Promise.resolve(
Model.countDocuments(query, { Model.countDocuments(query, {
...options,
hint: { _id: 1 }, hint: { _id: 1 },
}), }),
) )

View File

@@ -93,18 +93,13 @@ export function mongooseAdapter({
connectOptions, connectOptions,
disableIndexHints = false, disableIndexHints = false,
migrationDir: migrationDirArg, migrationDir: migrationDirArg,
transactionOptions, transactionOptions = {},
url, url,
}: Args): MongooseAdapterResult { }: Args): MongooseAdapterResult {
function adapter({ payload }: { payload: Payload }) { function adapter({ payload }: { payload: Payload }) {
const migrationDir = findMigrationDir(migrationDirArg) const migrationDir = findMigrationDir(migrationDirArg)
let beginTransactionFunction = beginTransaction
mongoose.set('strictQuery', false) mongoose.set('strictQuery', false)
if (transactionOptions === false) {
beginTransactionFunction = () => null
}
return createDatabaseAdapter<MongooseAdapter>({ return createDatabaseAdapter<MongooseAdapter>({
name: 'mongoose', name: 'mongoose',
@@ -122,7 +117,7 @@ export function mongooseAdapter({
versions: {}, versions: {},
// DatabaseAdapter // DatabaseAdapter
beginTransaction: beginTransactionFunction, beginTransaction: transactionOptions ? beginTransaction : undefined,
commitTransaction, commitTransaction,
connect, connect,
create, create,

View File

@@ -1,6 +1,6 @@
{ {
"name": "@payloadcms/db-postgres", "name": "@payloadcms/db-postgres",
"version": "0.3.1", "version": "0.4.0",
"description": "The officially supported Postgres database adapter for Payload", "description": "The officially supported Postgres database adapter for Payload",
"repository": "https://github.com/payloadcms/payload", "repository": "https://github.com/payloadcms/payload",
"license": "MIT", "license": "MIT",
@@ -22,8 +22,8 @@
"dependencies": { "dependencies": {
"@libsql/client": "^0.3.1", "@libsql/client": "^0.3.1",
"console-table-printer": "2.11.2", "console-table-printer": "2.11.2",
"drizzle-kit": "0.19.13-e99bac1", "drizzle-kit": "0.20.5-608ae62",
"drizzle-orm": "0.28.5", "drizzle-orm": "0.29.3",
"pg": "8.11.3", "pg": "8.11.3",
"prompts": "2.4.2", "prompts": "2.4.2",
"to-snake-case": "1.0.0", "to-snake-case": "1.0.0",

View File

@@ -18,8 +18,9 @@ export const connect: Connect = async function connect(this: PostgresAdapter, pa
try { try {
this.pool = new Pool(this.poolOptions) this.pool = new Pool(this.poolOptions)
await this.pool.connect() await this.pool.connect()
const logger = this.logger || false
this.drizzle = drizzle(this.pool, { schema: this.schema }) this.drizzle = drizzle(this.pool, { schema: this.schema, logger })
if (process.env.PAYLOAD_DROP_DATABASE === 'true') { if (process.env.PAYLOAD_DROP_DATABASE === 'true') {
this.payload.logger.info('---- DROPPING TABLES ----') this.payload.logger.info('---- DROPPING TABLES ----')
await this.drizzle.execute(sql`drop schema public cascade; await this.drizzle.execute(sql`drop schema public cascade;
@@ -39,7 +40,7 @@ export const connect: Connect = async function connect(this: PostgresAdapter, pa
) )
return return
const { pushSchema } = require('drizzle-kit/utils') const { pushSchema } = require('drizzle-kit/payload')
// This will prompt if clarifications are needed for Drizzle to push new schema // This will prompt if clarifications are needed for Drizzle to push new schema
const { apply, hasDataLoss, statementsToExecute, warnings } = await pushSchema( const { apply, hasDataLoss, statementsToExecute, warnings } = await pushSchema(
@@ -59,9 +60,9 @@ export const connect: Connect = async function connect(this: PostgresAdapter, pa
const { confirm: acceptWarnings } = await prompts( const { confirm: acceptWarnings } = await prompts(
{ {
name: 'confirm', name: 'confirm',
type: 'confirm',
initial: false, initial: false,
message, message,
type: 'confirm',
}, },
{ {
onCancel: () => { onCancel: () => {

View File

@@ -1,5 +1,5 @@
/* eslint-disable no-restricted-syntax, no-await-in-loop */ /* eslint-disable no-restricted-syntax, no-await-in-loop */
import type { DrizzleSnapshotJSON } from 'drizzle-kit/utils' import type { DrizzleSnapshotJSON } from 'drizzle-kit/payload'
import type { CreateMigration } from 'payload/database' import type { CreateMigration } from 'payload/database'
import fs from 'fs' import fs from 'fs'
@@ -60,7 +60,7 @@ export const createMigration: CreateMigration = async function createMigration(
fs.mkdirSync(dir) fs.mkdirSync(dir)
} }
const { generateDrizzleJson, generateMigration } = require('drizzle-kit/utils') const { generateDrizzleJson, generateMigration } = require('drizzle-kit/payload')
const [yyymmdd, hhmmss] = new Date().toISOString().split('T') const [yyymmdd, hhmmss] = new Date().toISOString().split('T')
const formattedDate = yyymmdd.replace(/\D/g, '') const formattedDate = yyymmdd.replace(/\D/g, '')
@@ -99,9 +99,9 @@ export const createMigration: CreateMigration = async function createMigration(
const { confirm: shouldCreateBlankMigration } = await prompts( const { confirm: shouldCreateBlankMigration } = await prompts(
{ {
name: 'confirm', name: 'confirm',
type: 'confirm',
initial: false, initial: false,
message: 'No schema changes detected. Would you like to create a blank migration file?', message: 'No schema changes detected. Would you like to create a blank migration file?',
type: 'confirm',
}, },
{ {
onCancel: () => { onCancel: () => {

View File

@@ -50,6 +50,7 @@ export function postgresAdapter(args: Args): PostgresAdapterResult {
drizzle: undefined, drizzle: undefined,
enums: {}, enums: {},
fieldConstraints: {}, fieldConstraints: {},
logger: args.logger,
pool: undefined, pool: undefined,
poolOptions: args.pool, poolOptions: args.pool,
push: args.push, push: args.push,

View File

@@ -80,7 +80,7 @@ export async function migrate(this: PostgresAdapter): Promise<void> {
} }
async function runMigrationFile(payload: Payload, migration: Migration, batch: number) { async function runMigrationFile(payload: Payload, migration: Migration, batch: number) {
const { generateDrizzleJson } = require('drizzle-kit/utils') const { generateDrizzleJson } = require('drizzle-kit/payload')
const start = Date.now() const start = Date.now()
const req = { payload } as PayloadRequest const req = { payload } as PayloadRequest

View File

@@ -1,46 +0,0 @@
// type GenerateMigration = (before: DrizzleSnapshotJSON, after: DrizzleSnapshotJSON) => string[]
// type GenerateDrizzleJSON = (schema: DrizzleSchemaExports) => DrizzleSnapshotJSON
// type PushDiff = (schema: DrizzleSchemaExports) => Promise<{ warnings: string[], apply: () => Promise<void> }>
// drizzle-kit@utils
import { drizzle } from 'drizzle-orm/node-postgres'
import { Pool } from 'pg'
async function generateUsage() {
const { generateDrizzleJson, generateMigration } = require('drizzle-kit/utils')
// @ts-expect-error Just TypeScript being broken // TODO: Open TypeScript issue
const schema = await import('./data/users')
// @ts-expect-error Just TypeScript being broken // TODO: Open TypeScript issue
const schemaAfter = await import('./data/users-after')
const drizzleJsonBefore = generateDrizzleJson(schema)
const drizzleJsonAfter = generateDrizzleJson(schemaAfter)
const sqlStatements = await generateMigration(drizzleJsonBefore, drizzleJsonAfter)
console.log(sqlStatements)
}
async function pushUsage() {
const { pushSchema } = require('drizzle-kit/utils')
// @ts-expect-error Just TypeScript being broken // TODO: Open TypeScript issue
const schemaAfter = await import('./data/users-after')
const db = drizzle(new Pool({ connectionString: '' }))
const response = await pushSchema(schemaAfter, db)
console.log('\n')
console.log('hasDataLoss: ', response.hasDataLoss)
console.log('warnings: ', response.warnings)
console.log('statements: ', response.statementsToExecute)
await response.apply()
process.exit(0)
}

View File

@@ -16,7 +16,10 @@ const getFlattenedFieldNames = (fields: Field[], prefix: string = ''): string[]
return fields.reduce((fieldsToUse, field) => { return fields.reduce((fieldsToUse, field) => {
let fieldPrefix = prefix let fieldPrefix = prefix
if (field.type === 'blocks') { if (
['array', 'blocks', 'relationship', 'upload'].includes(field.type) ||
('hasMany' in field && field.hasMany === true)
) {
return fieldsToUse return fieldsToUse
} }
@@ -54,29 +57,27 @@ export const validateExistingBlockIsIdentical = ({
rootTableName, rootTableName,
table, table,
}: Args): void => { }: Args): void => {
if (table) { const fieldNames = getFlattenedFieldNames(block.fields)
const fieldNames = getFlattenedFieldNames(block.fields)
const missingField = const missingField =
// ensure every field from the config is in the matching table // ensure every field from the config is in the matching table
fieldNames.find((name) => Object.keys(table).indexOf(name) === -1) || fieldNames.find((name) => Object.keys(table).indexOf(name) === -1) ||
// ensure every table column is matched for every field from the config // ensure every table column is matched for every field from the config
Object.keys(table).find((fieldName) => { Object.keys(table).find((fieldName) => {
if (!['_locale', '_order', '_parentID', '_path', '_uuid'].includes(fieldName)) { if (!['_locale', '_order', '_parentID', '_path', '_uuid'].includes(fieldName)) {
return fieldNames.indexOf(fieldName) === -1 return fieldNames.indexOf(fieldName) === -1
} }
}) })
if (missingField) { if (missingField) {
throw new InvalidConfiguration( throw new InvalidConfiguration(
`The table ${rootTableName} has multiple blocks with slug ${block.slug}, but the schemas do not match. One block includes the field ${missingField}, while the other block does not.`, `The table ${rootTableName} has multiple blocks with slug ${block.slug}, but the schemas do not match. One block includes the field ${missingField}, while the other block does not.`,
) )
} }
if (Boolean(localized) !== Boolean(table._locale)) { if (Boolean(localized) !== Boolean(table._locale)) {
throw new InvalidConfiguration( throw new InvalidConfiguration(
`The table ${rootTableName} has multiple blocks with slug ${block.slug}, but the schemas do not match. One is localized, but another is not. Block schemas of the same name must match exactly.`, `The table ${rootTableName} has multiple blocks with slug ${block.slug}, but the schemas do not match. One is localized, but another is not. Block schemas of the same name must match exactly.`,
) )
}
} }
} }

View File

@@ -7,8 +7,8 @@ import { fieldAffectsData } from 'payload/types'
import type { BlocksMap } from '../../utilities/createBlocksMap' import type { BlocksMap } from '../../utilities/createBlocksMap'
import { transformHasManyNumber } from './hasManyNumber' import { transformHasManyNumber } from './hasManyNumber'
import { transformRelationship } from './relationship'
import { transformHasManyText } from './hasManyText' import { transformHasManyText } from './hasManyText'
import { transformRelationship } from './relationship'
type TraverseFieldsArgs = { type TraverseFieldsArgs = {
/** /**
@@ -35,10 +35,6 @@ type TraverseFieldsArgs = {
* An array of Payload fields to traverse * An array of Payload fields to traverse
*/ */
fields: (Field | TabAsField)[] fields: (Field | TabAsField)[]
/**
* All hasMany text fields, as returned by Drizzle, keyed on an object by field path
*/
texts: Record<string, Record<string, unknown>[]>
/** /**
* All hasMany number fields, as returned by Drizzle, keyed on an object by field path * All hasMany number fields, as returned by Drizzle, keyed on an object by field path
*/ */
@@ -55,6 +51,10 @@ type TraverseFieldsArgs = {
* Data structure representing the nearest table from db * Data structure representing the nearest table from db
*/ */
table: Record<string, unknown> table: Record<string, unknown>
/**
* All hasMany text fields, as returned by Drizzle, keyed on an object by field path
*/
texts: Record<string, Record<string, unknown>[]>
} }
// Traverse fields recursively, transforming data // Traverse fields recursively, transforming data
@@ -66,11 +66,11 @@ export const traverseFields = <T extends Record<string, unknown>>({
deletions, deletions,
fieldPrefix, fieldPrefix,
fields, fields,
texts,
numbers, numbers,
path, path,
relationships, relationships,
table, table,
texts,
}: TraverseFieldsArgs): T => { }: TraverseFieldsArgs): T => {
const sanitizedPath = path ? `${path}.` : path const sanitizedPath = path ? `${path}.` : path
@@ -83,11 +83,11 @@ export const traverseFields = <T extends Record<string, unknown>>({
deletions, deletions,
fieldPrefix, fieldPrefix,
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })), fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
texts,
numbers, numbers,
path, path,
relationships, relationships,
table, table,
texts,
}) })
} }
@@ -103,17 +103,22 @@ export const traverseFields = <T extends Record<string, unknown>>({
deletions, deletions,
fieldPrefix, fieldPrefix,
fields: field.fields, fields: field.fields,
texts,
numbers, numbers,
path, path,
relationships, relationships,
table, table,
texts,
}) })
} }
if (fieldAffectsData(field)) { if (fieldAffectsData(field)) {
const fieldName = `${fieldPrefix || ''}${field.name}` const fieldName = `${fieldPrefix || ''}${field.name}`
const fieldData = table[fieldName] const fieldData = table[fieldName]
if (fieldPrefix) {
deletions.push(() => delete table[fieldName])
}
if (field.type === 'array') { if (field.type === 'array') {
if (Array.isArray(fieldData)) { if (Array.isArray(fieldData)) {
if (field.localized) { if (field.localized) {
@@ -135,13 +140,17 @@ export const traverseFields = <T extends Record<string, unknown>>({
deletions, deletions,
fieldPrefix: '', fieldPrefix: '',
fields: field.fields, fields: field.fields,
texts,
numbers, numbers,
path: `${sanitizedPath}${field.name}.${row._order - 1}`, path: `${sanitizedPath}${field.name}.${row._order - 1}`,
relationships, relationships,
table: row, table: row,
texts,
}) })
if ('_order' in rowResult) {
delete rowResult._order
}
arrayResult[locale].push(rowResult) arrayResult[locale].push(rowResult)
} }
@@ -153,6 +162,11 @@ export const traverseFields = <T extends Record<string, unknown>>({
row.id = row._uuid row.id = row._uuid
delete row._uuid delete row._uuid
} }
if ('_order' in row) {
delete row._order
}
return traverseFields<T>({ return traverseFields<T>({
blocks, blocks,
config, config,
@@ -160,11 +174,11 @@ export const traverseFields = <T extends Record<string, unknown>>({
deletions, deletions,
fieldPrefix: '', fieldPrefix: '',
fields: field.fields, fields: field.fields,
texts,
numbers, numbers,
path: `${sanitizedPath}${field.name}.${i}`, path: `${sanitizedPath}${field.name}.${i}`,
relationships, relationships,
table: row, table: row,
texts,
}) })
}) })
} }
@@ -204,11 +218,11 @@ export const traverseFields = <T extends Record<string, unknown>>({
deletions, deletions,
fieldPrefix: '', fieldPrefix: '',
fields: block.fields, fields: block.fields,
texts,
numbers, numbers,
path: `${blockFieldPath}.${row._order - 1}`, path: `${blockFieldPath}.${row._order - 1}`,
relationships, relationships,
table: row, table: row,
texts,
}) })
delete blockResult._order delete blockResult._order
@@ -235,11 +249,11 @@ export const traverseFields = <T extends Record<string, unknown>>({
deletions, deletions,
fieldPrefix: '', fieldPrefix: '',
fields: block.fields, fields: block.fields,
texts,
numbers, numbers,
path: `${blockFieldPath}.${i}`, path: `${blockFieldPath}.${i}`,
relationships, relationships,
table: row, table: row,
texts,
}) })
} }
@@ -316,15 +330,15 @@ export const traverseFields = <T extends Record<string, unknown>>({
transformHasManyText({ transformHasManyText({
field, field,
locale, locale,
textRows: texts,
ref: result, ref: result,
textRows: texts,
}) })
}) })
} else { } else {
transformHasManyText({ transformHasManyText({
field, field,
textRows: textPathMatch,
ref: result, ref: result,
textRows: textPathMatch,
}) })
} }
@@ -420,13 +434,16 @@ export const traverseFields = <T extends Record<string, unknown>>({
deletions, deletions,
fieldPrefix: groupFieldPrefix, fieldPrefix: groupFieldPrefix,
fields: field.fields, fields: field.fields,
texts,
numbers, numbers,
path: `${sanitizedPath}${field.name}`, path: `${sanitizedPath}${field.name}`,
relationships, relationships,
table, table,
texts,
}) })
}) })
if ('_order' in ref) {
delete ref._order
}
} else { } else {
const groupData = {} const groupData = {}
@@ -437,12 +454,15 @@ export const traverseFields = <T extends Record<string, unknown>>({
deletions, deletions,
fieldPrefix: groupFieldPrefix, fieldPrefix: groupFieldPrefix,
fields: field.fields, fields: field.fields,
texts,
numbers, numbers,
path: `${sanitizedPath}${field.name}`, path: `${sanitizedPath}${field.name}`,
relationships, relationships,
table, table,
texts,
}) })
if ('_order' in ref) {
delete ref._order
}
} }
break break

View File

@@ -1,6 +1,7 @@
import type { import type {
ColumnBaseConfig, ColumnBaseConfig,
ColumnDataType, ColumnDataType,
DrizzleConfig,
ExtractTablesWithRelations, ExtractTablesWithRelations,
Relation, Relation,
Relations, Relations,
@@ -16,6 +17,7 @@ export type DrizzleDB = NodePgDatabase<Record<string, unknown>>
export type Args = { export type Args = {
migrationDir?: string migrationDir?: string
pool: PoolConfig pool: PoolConfig
logger?: DrizzleConfig['logger']
push?: boolean push?: boolean
} }
@@ -47,6 +49,7 @@ export type DrizzleTransaction = PgTransaction<
export type PostgresAdapter = BaseDatabaseAdapter & { export type PostgresAdapter = BaseDatabaseAdapter & {
drizzle: DrizzleDB drizzle: DrizzleDB
logger: DrizzleConfig['logger']
enums: Record<string, GenericEnum> enums: Record<string, GenericEnum>
pool: Pool pool: Pool
poolOptions: Args['pool'] poolOptions: Args['pool']

View File

@@ -1,6 +1,6 @@
{ {
"name": "payload", "name": "payload",
"version": "2.8.0", "version": "2.8.2",
"description": "Node, React and MongoDB Headless CMS and Application Framework", "description": "Node, React and MongoDB Headless CMS and Application Framework",
"license": "MIT", "license": "MIT",
"main": "./dist/index.js", "main": "./dist/index.js",

View File

@@ -13,7 +13,6 @@
& > .field-type { & > .field-type {
margin-bottom: var(--spacing-field); margin-bottom: var(--spacing-field);
max-width: 100%;
&[type='hidden'] { &[type='hidden'] {
margin-bottom: 0; margin-bottom: 0;

View File

@@ -2,6 +2,7 @@
.upload { .upload {
position: relative; position: relative;
max-width: 100%;
&__wrap { &__wrap {
background: var(--theme-elevation-50); background: var(--theme-elevation-50);

View File

@@ -1,10 +1,9 @@
import payload from '..'
import loadConfig from '../config/load' import loadConfig from '../config/load'
export const build = async (): Promise<void> => { export const build = async (): Promise<void> => {
const config = await loadConfig() // Will throw its own error if it fails const config = await loadConfig() // Will throw its own error if it fails
await payload.config.admin.bundler.build(config) await config.admin.bundler.build(config)
} }
// when build.js is launched directly // when build.js is launched directly

View File

@@ -577,7 +577,7 @@ export type Block = {
*/ */
interfaceName?: string interfaceName?: string
labels?: Labels labels?: Labels
slug: string, slug: string
/** Extension point to add your custom data. */ /** Extension point to add your custom data. */
custom?: Record<string, any> custom?: Record<string, any>
} }

View File

@@ -1,7 +1,7 @@
{ {
"name": "@payloadcms/plugin-form-builder", "name": "@payloadcms/plugin-form-builder",
"description": "Form builder plugin for Payload CMS", "description": "Form builder plugin for Payload CMS",
"version": "1.1.1", "version": "1.1.2",
"homepage:": "https://payloadcms.com", "homepage:": "https://payloadcms.com",
"repository": "git@github.com:payloadcms/plugin-form-builder.git", "repository": "git@github.com:payloadcms/plugin-form-builder.git",
"main": "dist/index.js", "main": "dist/index.js",

View File

@@ -7,6 +7,8 @@ import sendEmail from './hooks/sendEmail'
// all settings can be overridden by the config // all settings can be overridden by the config
export const generateSubmissionCollection = (formConfig: PluginConfig): CollectionConfig => { export const generateSubmissionCollection = (formConfig: PluginConfig): CollectionConfig => {
const formSlug = formConfig?.formOverrides?.slug || 'forms'
const newConfig: CollectionConfig = { const newConfig: CollectionConfig = {
...(formConfig?.formSubmissionOverrides || {}), ...(formConfig?.formSubmissionOverrides || {}),
access: { access: {
@@ -25,9 +27,28 @@ export const generateSubmissionCollection = (formConfig: PluginConfig): Collecti
admin: { admin: {
readOnly: true, readOnly: true,
}, },
relationTo: formConfig?.formOverrides?.slug || 'forms', relationTo: formSlug,
required: true, required: true,
type: 'relationship', type: 'relationship',
validate: async (value, { payload }) => {
/* Don't run in the client side */
if (!payload) return true
if (payload) {
let existingForm
try {
existingForm = await payload.findByID({
id: value,
collection: formSlug,
})
return true
} catch (error) {
return 'Cannot create this submission because this form does not exist.'
}
}
},
}, },
{ {
name: 'submissionData', name: 'submissionData',

View File

@@ -22,7 +22,7 @@ type MetaDescriptionProps = TextareaField & {
} }
export const MetaDescription: React.FC<MetaDescriptionProps> = (props) => { export const MetaDescription: React.FC<MetaDescriptionProps> = (props) => {
const { name, label, path, pluginConfig } = props const { name, label, path, pluginConfig, required } = props
const { t } = useTranslation('plugin-seo') const { t } = useTranslation('plugin-seo')
@@ -36,7 +36,7 @@ export const MetaDescription: React.FC<MetaDescriptionProps> = (props) => {
path, path,
} as Options) } as Options)
const { setValue, showError, value } = field const { setValue, showError, value, errorMessage } = field
const regenerateDescription = useCallback(async () => { const regenerateDescription = useCallback(async () => {
const { generateDescription } = pluginConfig const { generateDescription } = pluginConfig
@@ -67,6 +67,18 @@ export const MetaDescription: React.FC<MetaDescriptionProps> = (props) => {
> >
<div> <div>
{label && typeof label === 'string' && label} {label && typeof label === 'string' && label}
{required && (
<span
style={{
marginLeft: '5px',
color: 'var(--theme-error-500)',
}}
>
*
</span>
)}
{typeof pluginConfig.generateDescription === 'function' && ( {typeof pluginConfig.generateDescription === 'function' && (
<React.Fragment> <React.Fragment>
&nbsp; &mdash; &nbsp; &nbsp; &mdash; &nbsp;
@@ -117,6 +129,8 @@ export const MetaDescription: React.FC<MetaDescriptionProps> = (props) => {
style={{ style={{
marginBottom: 0, marginBottom: 0,
}} }}
required={required}
errorMessage={errorMessage}
value={value} value={value}
/> />
</div> </div>

View File

@@ -19,7 +19,7 @@ type MetaImageProps = UploadInputProps & {
} }
export const MetaImage: React.FC<MetaImageProps> = (props) => { export const MetaImage: React.FC<MetaImageProps> = (props) => {
const { name, fieldTypes, label, pluginConfig, relationTo } = props || {} const { name, fieldTypes, label, pluginConfig, relationTo, required } = props || {}
const field: FieldType<string> = useField(props as Options) const field: FieldType<string> = useField(props as Options)
@@ -29,7 +29,7 @@ export const MetaImage: React.FC<MetaImageProps> = (props) => {
const [fields] = useAllFormFields() const [fields] = useAllFormFields()
const docInfo = useDocumentInfo() const docInfo = useDocumentInfo()
const { setValue, showError, value } = field const { setValue, showError, value, errorMessage } = field
const regenerateImage = useCallback(async () => { const regenerateImage = useCallback(async () => {
const { generateImage } = pluginConfig const { generateImage } = pluginConfig
@@ -68,6 +68,18 @@ export const MetaImage: React.FC<MetaImageProps> = (props) => {
> >
<div> <div>
{label && typeof label === 'string' && label} {label && typeof label === 'string' && label}
{required && (
<span
style={{
marginLeft: '5px',
color: 'var(--theme-error-500)',
}}
>
*
</span>
)}
{typeof pluginConfig.generateImage === 'function' && ( {typeof pluginConfig.generateImage === 'function' && (
<React.Fragment> <React.Fragment>
&nbsp; &mdash; &nbsp; &nbsp; &mdash; &nbsp;
@@ -110,6 +122,8 @@ export const MetaImage: React.FC<MetaImageProps> = (props) => {
collection={collection} collection={collection}
fieldTypes={fieldTypes} fieldTypes={fieldTypes}
filterOptions={{}} filterOptions={{}}
errorMessage={errorMessage}
required={required}
label={undefined} label={undefined}
name={name} name={name}
onChange={(incomingImage) => { onChange={(incomingImage) => {

View File

@@ -25,7 +25,7 @@ type MetaTitleProps = TextFieldType & {
} }
export const MetaTitle: React.FC<MetaTitleProps> = (props) => { export const MetaTitle: React.FC<MetaTitleProps> = (props) => {
const { name, label, path, pluginConfig } = props || {} const { name, label, path, pluginConfig, required } = props || {}
const { t } = useTranslation('plugin-seo') const { t } = useTranslation('plugin-seo')
@@ -39,7 +39,7 @@ export const MetaTitle: React.FC<MetaTitleProps> = (props) => {
const [fields] = useAllFormFields() const [fields] = useAllFormFields()
const docInfo = useDocumentInfo() const docInfo = useDocumentInfo()
const { setValue, showError, value } = field const { setValue, showError, value, errorMessage } = field
const regenerateTitle = useCallback(async () => { const regenerateTitle = useCallback(async () => {
const { generateTitle } = pluginConfig const { generateTitle } = pluginConfig
@@ -70,6 +70,18 @@ export const MetaTitle: React.FC<MetaTitleProps> = (props) => {
> >
<div> <div>
{label && typeof label === 'string' && label} {label && typeof label === 'string' && label}
{required && (
<span
style={{
marginLeft: '5px',
color: 'var(--theme-error-500)',
}}
>
*
</span>
)}
{typeof pluginConfig.generateTitle === 'function' && ( {typeof pluginConfig.generateTitle === 'function' && (
<React.Fragment> <React.Fragment>
&nbsp; &mdash; &nbsp; &nbsp; &mdash; &nbsp;
@@ -121,6 +133,8 @@ export const MetaTitle: React.FC<MetaTitleProps> = (props) => {
style={{ style={{
marginBottom: 0, marginBottom: 0,
}} }}
errorMessage={errorMessage}
required={required}
value={value} value={value}
/> />
</div> </div>

View File

@@ -30,6 +30,7 @@ const seo =
}, },
label: 'Overview', label: 'Overview',
}, },
// @ts-expect-error
{ {
name: 'title', name: 'title',
type: 'text', type: 'text',
@@ -39,6 +40,7 @@ const seo =
}, },
}, },
localized: true, localized: true,
...(pluginConfig?.fieldOverrides?.title ?? {}),
}, },
{ {
name: 'description', name: 'description',
@@ -49,6 +51,7 @@ const seo =
}, },
}, },
localized: true, localized: true,
...(pluginConfig?.fieldOverrides?.description ?? {}),
}, },
...(pluginConfig?.uploadsCollection ...(pluginConfig?.uploadsCollection
? [ ? [
@@ -66,6 +69,7 @@ const seo =
label: 'Meta Image', label: 'Meta Image',
localized: true, localized: true,
relationTo: pluginConfig?.uploadsCollection, relationTo: pluginConfig?.uploadsCollection,
...(pluginConfig?.fieldOverrides?.image ?? {}),
} as Field, } as Field,
] ]
: []), : []),
@@ -81,6 +85,7 @@ const seo =
label: 'Preview', label: 'Preview',
}, },
], ],
interfaceName: pluginConfig.interfaceName,
label: 'SEO', label: 'SEO',
}, },
] ]

View File

@@ -1,5 +1,5 @@
import type { ContextType } from 'payload/dist/admin/components/utilities/DocumentInfo/types' import type { ContextType } from 'payload/dist/admin/components/utilities/DocumentInfo/types'
import type { Field } from 'payload/dist/fields/config/types' import type { Field, TextareaField, TextField, UploadField } from 'payload/dist/fields/config/types'
export type GenerateTitle = <T = any>( export type GenerateTitle = <T = any>(
args: ContextType & { doc: T; locale?: string }, args: ContextType & { doc: T; locale?: string },
@@ -29,6 +29,12 @@ export interface PluginConfig {
generateURL?: GenerateURL generateURL?: GenerateURL
globals?: string[] globals?: string[]
tabbedUI?: boolean tabbedUI?: boolean
fieldOverrides?: {
title?: Partial<TextField>
description?: Partial<TextareaField>
image?: Partial<UploadField>
}
interfaceName?: string
uploadsCollection?: string uploadsCollection?: string
} }

View File

@@ -37,7 +37,7 @@ export const Preview: React.FC<PreviewProps> = (props) => {
if (typeof generateURL === 'function' && !href) { if (typeof generateURL === 'function' && !href) {
const newHref = await generateURL({ const newHref = await generateURL({
...docInfo, ...docInfo,
doc: { fields }, doc: { ...fields },
locale: typeof locale === 'object' ? locale?.code : locale, locale: typeof locale === 'object' ? locale?.code : locale,
}) })

View File

@@ -1,6 +1,6 @@
{ {
"name": "@payloadcms/richtext-lexical", "name": "@payloadcms/richtext-lexical",
"version": "0.5.1", "version": "0.5.2",
"description": "The officially supported Lexical richtext adapter for Payload", "description": "The officially supported Lexical richtext adapter for Payload",
"repository": "https://github.com/payloadcms/payload", "repository": "https://github.com/payloadcms/payload",
"license": "MIT", "license": "MIT",

632
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -41,7 +41,7 @@ export default buildConfigWithDefaults({
{ {
name: 'blockTwoField', name: 'blockTwoField',
type: 'text', type: 'text',
} },
], ],
custom: { description: 'The blockOne of this page' }, custom: { description: 'The blockOne of this page' },
}, },

View File

@@ -1,6 +1,6 @@
import payload from '../../packages/payload/src' import payload from '../../packages/payload/src'
import { initPayloadTest } from '../helpers/configHelpers' import { initPayloadTest } from '../helpers/configHelpers'
import { BlockField } from "payload/dist/fields/config/types"; import { BlockField } from 'payload/dist/fields/config/types'
require('isomorphic-fetch') require('isomorphic-fetch')
@@ -39,7 +39,7 @@ describe('Config', () => {
it('allows a custom field in collection fields', () => { it('allows a custom field in collection fields', () => {
const [collection] = payload.config.collections const [collection] = payload.config.collections
const [field,] = collection.fields const [field] = collection.fields
expect(field.custom).toEqual({ description: 'The title of this page' }) expect(field.custom).toEqual({ description: 'The title of this page' })
}) })
@@ -48,7 +48,9 @@ describe('Config', () => {
const [collection] = payload.config.collections const [collection] = payload.config.collections
const [, blocksField] = collection.fields const [, blocksField] = collection.fields
expect((blocksField as BlockField).blocks[0].custom).toEqual({ description: 'The blockOne of this page' }) expect((blocksField as BlockField).blocks[0].custom).toEqual({
description: 'The blockOne of this page',
})
}) })
}) })

View File

@@ -8,77 +8,76 @@
export interface Config { export interface Config {
collections: { collections: {
pages: Page; pages: Page
users: User; users: User
'payload-preferences': PayloadPreference; 'payload-preferences': PayloadPreference
'payload-migrations': PayloadMigration; 'payload-migrations': PayloadMigration
}; }
globals: { globals: {
'my-global': MyGlobal; 'my-global': MyGlobal
}; }
} }
export interface Page { export interface Page {
id: string; id: string
title?: string | null; title?: string | null
myBlocks?: myBlocks?:
| { | {
blockOneField?: string | null; blockOneField?: string | null
blockTwoField?: string | null; blockTwoField?: string | null
id?: string | null; id?: string | null
blockName?: string | null; blockName?: string | null
blockType: 'blockOne'; blockType: 'blockOne'
}[] }[]
| null; | null
updatedAt: string; updatedAt: string
createdAt: string; createdAt: string
} }
export interface User { export interface User {
id: string; id: string
updatedAt: string; updatedAt: string
createdAt: string; createdAt: string
email: string; email: string
resetPasswordToken?: string | null; resetPasswordToken?: string | null
resetPasswordExpiration?: string | null; resetPasswordExpiration?: string | null
salt?: string | null; salt?: string | null
hash?: string | null; hash?: string | null
loginAttempts?: number | null; loginAttempts?: number | null
lockUntil?: string | null; lockUntil?: string | null
password: string | null; password: string | null
} }
export interface PayloadPreference { export interface PayloadPreference {
id: string; id: string
user: { user: {
relationTo: 'users'; relationTo: 'users'
value: string | User; value: string | User
}; }
key?: string | null; key?: string | null
value?: value?:
| { | {
[k: string]: unknown; [k: string]: unknown
} }
| unknown[] | unknown[]
| string | string
| number | number
| boolean | boolean
| null; | null
updatedAt: string; updatedAt: string
createdAt: string; createdAt: string
} }
export interface PayloadMigration { export interface PayloadMigration {
id: string; id: string
name?: string | null; name?: string | null
batch?: number | null; batch?: number | null
updatedAt: string; updatedAt: string
createdAt: string; createdAt: string
} }
export interface MyGlobal { export interface MyGlobal {
id: string; id: string
title?: string | null; title?: string | null
updatedAt?: string | null; updatedAt?: string | null
createdAt?: string | null; createdAt?: string | null
} }
declare module 'payload' { declare module 'payload' {
export interface GeneratedTypes extends Config {} export interface GeneratedTypes extends Config {}
} }

View File

@@ -246,6 +246,22 @@ const BlockFields: CollectionConfig = {
}, },
], ],
}, },
{
slug: 'block-b',
fields: [
{
name: 'items',
type: 'array',
fields: [
{
name: 'title2',
type: 'text',
required: true,
},
],
},
],
},
], ],
}, },
{ {

View File

@@ -58,6 +58,24 @@ const GroupFields: CollectionConfig = {
}, },
], ],
}, },
{
name: 'arrayOfGroups',
type: 'array',
defaultValue: [
{
groupItem: {
text: 'Hello world',
},
},
],
fields: [
{
name: 'groupItem',
type: 'group',
fields: [{ name: 'text', type: 'text' }],
},
],
},
{ {
name: 'potentiallyEmptyGroup', name: 'potentiallyEmptyGroup',
type: 'group', type: 'group',

View File

@@ -112,7 +112,7 @@ describe('Fields', () => {
}) })
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-expect-error
expect(localizedDoc.localizedHasMany.en).toEqual(localizedHasMany) expect(localizedDoc.localizedHasMany.en).toEqual(localizedHasMany)
}) })
}) })
@@ -413,7 +413,7 @@ describe('Fields', () => {
}) })
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-expect-error
expect(localizedDoc.localizedHasMany.en).toEqual(localizedHasMany) expect(localizedDoc.localizedHasMany.en).toEqual(localizedHasMany)
}) })
}) })
@@ -643,6 +643,14 @@ describe('Fields', () => {
collection, collection,
}) })
expect(result.items[0]).toMatchObject({
subArray: [
{
text: subArrayText,
},
],
text: 'test',
})
expect(result.items[0].subArray[0].text).toStrictEqual(subArrayText) expect(result.items[0].subArray[0].text).toStrictEqual(subArrayText)
}) })
@@ -707,6 +715,15 @@ describe('Fields', () => {
expect(document.group.defaultParent).toStrictEqual(groupDefaultValue) expect(document.group.defaultParent).toStrictEqual(groupDefaultValue)
expect(document.group.defaultChild).toStrictEqual(groupDefaultChild) expect(document.group.defaultChild).toStrictEqual(groupDefaultChild)
}) })
it('should not have duplicate keys', async () => {
expect(document.arrayOfGroups[0]).toMatchObject({
id: expect.any(String),
groupItem: {
text: 'Hello world',
},
})
})
}) })
describe('tabs', () => { describe('tabs', () => {

View File

@@ -5,6 +5,7 @@ import { serializeLexical } from '../../packages/plugin-form-builder/src/utiliti
import { serializeSlate } from '../../packages/plugin-form-builder/src/utilities/slate/serializeSlate' import { serializeSlate } from '../../packages/plugin-form-builder/src/utilities/slate/serializeSlate'
import { initPayloadTest } from '../helpers/configHelpers' import { initPayloadTest } from '../helpers/configHelpers'
import { formSubmissionsSlug, formsSlug } from './shared' import { formSubmissionsSlug, formsSlug } from './shared'
import { ValidationError } from '../../packages/payload/src/errors'
describe('Form Builder Plugin', () => { describe('Form Builder Plugin', () => {
let form: Form let form: Form
@@ -42,7 +43,7 @@ describe('Form Builder Plugin', () => {
it('adds form submissions collection', async () => { it('adds form submissions collection', async () => {
const { docs: formSubmissions } = await payload.find({ collection: formSubmissionsSlug }) const { docs: formSubmissions } = await payload.find({ collection: formSubmissionsSlug })
expect(formSubmissions).toHaveLength(0) expect(formSubmissions).toHaveLength(1)
}) })
}) })
@@ -118,6 +119,25 @@ describe('Form Builder Plugin', () => {
expect(formSubmission.submissionData[0]).toHaveProperty('value', 'Test Submission') expect(formSubmission.submissionData[0]).toHaveProperty('value', 'Test Submission')
}) })
it('does not create a form submission for a non-existing form', async () => {
const req = async () =>
payload.create({
collection: formSubmissionsSlug,
data: {
form: '659c7c2f98ffb5d83df9dadb',
submissionData: [
{
field: 'name',
value: 'Test Submission',
},
],
},
depth: 0,
})
await expect(req).rejects.toThrow(ValidationError)
})
it('replaces curly braces with data when using slate serializer', async () => { it('replaces curly braces with data when using slate serializer', async () => {
const mockName = 'Test Submission' const mockName = 'Test Submission'
const mockEmail = 'dev@payloadcms.com' const mockEmail = 'dev@payloadcms.com'

View File

@@ -1,7 +1,7 @@
import type { Payload } from '../../../packages/payload/src' import type { Payload } from '../../../packages/payload/src'
import type { PayloadRequest } from '../../../packages/payload/src/express/types' import type { PayloadRequest } from '../../../packages/payload/src/express/types'
import { formsSlug, pagesSlug } from '../shared' import { formSubmissionsSlug, formsSlug, pagesSlug } from '../shared'
export const seed = async (payload: Payload): Promise<boolean> => { export const seed = async (payload: Payload): Promise<boolean> => {
payload.logger.info('Seeding data...') payload.logger.info('Seeding data...')
@@ -53,6 +53,23 @@ export const seed = async (payload: Payload): Promise<boolean> => {
}, },
}) })
await payload.create({
collection: formSubmissionsSlug,
data: {
form: formID,
submissionData: [
{
field: 'name',
value: 'Test Submission',
},
{
field: 'email',
value: 'tester@example.com',
},
],
},
})
await payload.create({ await payload.create({
collection: pagesSlug, collection: pagesSlug,
data: { data: {

View File

@@ -51,6 +51,11 @@ export default buildConfigWithDefaults({
label: 'og:title', label: 'og:title',
}, },
], ],
fieldOverrides: {
title: {
required: true,
},
},
generateTitle: (data: any) => `Website.com — ${data?.doc?.title?.value}`, generateTitle: (data: any) => `Website.com — ${data?.doc?.title?.value}`,
generateDescription: ({ doc }: any) => doc?.excerpt?.value, generateDescription: ({ doc }: any) => doc?.excerpt?.value,
generateURL: ({ doc, locale }: any) => generateURL: ({ doc, locale }: any) =>

View File

@@ -78,6 +78,15 @@ describe('SEO Plugin', () => {
await expect(metaTitle).toHaveValue('Website.com — Test Page') await expect(metaTitle).toHaveValue('Website.com — Test Page')
}) })
// todo: Re-enable this test once required attributes are fixed
/* test('Title should be required as per custom override', async () => {
const metaTitleClass = '#field-title'
const metaTitle = page.locator(metaTitleClass).nth(0)
await expect(metaTitle).toHaveAttribute('required', '')
}) */
test('Indicator should be orangered and characters counted', async () => { test('Indicator should be orangered and characters counted', async () => {
const indicatorClass = const indicatorClass =
'#field-meta > div > div.render-fields.render-fields--margins-small > div:nth-child(2) > div:nth-child(3) > div > div:nth-child(3) > div' '#field-meta > div > div.render-fields.render-fields--margins-small > div:nth-child(2) > div:nth-child(3) > div > div:nth-child(3) > div'

View File

@@ -31,6 +31,9 @@ describe('SEO Plugin', () => {
data: { data: {
title: 'Test page', title: 'Test page',
slug: 'test-page', slug: 'test-page',
meta: {
title: 'Test page',
},
}, },
depth: 0, depth: 0,
}) })