Compare commits
7 Commits
docs-hooks
...
fix/plugin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d36e23311 | ||
|
|
fdab2712c0 | ||
|
|
a231a05b7c | ||
|
|
b99c324f1e | ||
|
|
426f99ca99 | ||
|
|
4600c94cac | ||
|
|
c1b4960795 |
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@@ -160,6 +160,13 @@
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm tsx --no-deprecation test/dev.ts plugin-search",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev plugin-search",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run test:int live-preview",
|
||||
"cwd": "${workspaceFolder}",
|
||||
|
||||
@@ -269,11 +269,13 @@ Lexical does not generate accurate type definitions for your richText fields for
|
||||
|
||||
The Rich Text Field editor configuration has an `admin` property with the following options:
|
||||
|
||||
| Property | Description |
|
||||
| ------------------------------ | ---------------------------------------------------------------------------------------- |
|
||||
| **`placeholder`** | Set this property to define a placeholder string for the field. |
|
||||
| **`hideGutter`** | Set this property to `true` to hide this field's gutter within the Admin Panel. |
|
||||
| **`hideInsertParagraphAtEnd`** | Set this property to `true` to hide the "+" button that appears at the end of the editor |
|
||||
| Property | Description |
|
||||
| ------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
||||
| **`placeholder`** | Set this property to define a placeholder string for the field. |
|
||||
| **`hideGutter`** | Set this property to `true` to hide this field's gutter within the Admin Panel. |
|
||||
| **`hideInsertParagraphAtEnd`** | Set this property to `true` to hide the "+" button that appears at the end of the editor. |
|
||||
| **`hideDraggableBlockElement`** | Set this property to `true` to hide the draggable element that appears when you hover a node in the editor. |
|
||||
| **`hideAddBlockButton`** | Set this property to `true` to hide the "+" button that appears when you hover a node in the editor. |
|
||||
|
||||
### Disable the gutter
|
||||
|
||||
|
||||
1
next-env.d.ts
vendored
1
next-env.d.ts
vendored
@@ -1,5 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
/// <reference path="./.next/types/routes.d.ts" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload-monorepo",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"workspaces": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/admin-bar",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "An admin bar for React apps using Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-payload-app",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "The officially supported MongoDB database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-sqlite",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "The officially supported SQLite database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-vercel-postgres",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "Vercel Postgres adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/drizzle",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "A library of shared functions used by different payload database adapters",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-nodemailer",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "Payload Nodemailer Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-resend",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "Payload Resend Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/graphql",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview-react",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "The official React SDK for Payload Live Preview",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview-vue",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "The official Vue SDK for Payload Live Preview",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "The official live preview JavaScript SDK for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/next",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/payload-cloud",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "The official Payload Cloud plugin",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "Node, React, Headless CMS and Application Framework built on Next.js",
|
||||
"keywords": [
|
||||
"admin panel",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-cloud-storage",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "The official cloud storage plugin for Payload CMS",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-form-builder",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "Form builder plugin for Payload CMS",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-import-export",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "Import-Export plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-multi-tenant",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "Multi Tenant plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-nested-docs",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "The official Nested Docs plugin for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { CollectionBeforeChangeHook } from 'payload'
|
||||
|
||||
import type { NestedDocsPluginConfig } from '../types.js'
|
||||
|
||||
import { populateBreadcrumbs } from '../utilities/populateBreadcrumbs.js'
|
||||
|
||||
export const populateBreadcrumbsBeforeChange =
|
||||
(pluginConfig: NestedDocsPluginConfig): CollectionBeforeChangeHook =>
|
||||
async ({ collection, data, originalDoc, req }) =>
|
||||
populateBreadcrumbs({
|
||||
breadcrumbsFieldName: pluginConfig.breadcrumbsFieldSlug,
|
||||
collection,
|
||||
data,
|
||||
generateLabel: pluginConfig.generateLabel,
|
||||
generateURL: pluginConfig.generateURL,
|
||||
originalDoc,
|
||||
parentFieldName: pluginConfig.parentFieldSlug,
|
||||
req,
|
||||
})
|
||||
@@ -1,10 +1,4 @@
|
||||
import type {
|
||||
CollectionAfterChangeHook,
|
||||
CollectionConfig,
|
||||
JsonObject,
|
||||
PayloadRequest,
|
||||
ValidationError,
|
||||
} from 'payload'
|
||||
import type { CollectionAfterChangeHook, JsonObject, ValidationError } from 'payload'
|
||||
|
||||
import { APIError, ValidationErrorName } from 'payload'
|
||||
|
||||
@@ -12,21 +6,16 @@ import type { NestedDocsPluginConfig } from '../types.js'
|
||||
|
||||
import { populateBreadcrumbs } from '../utilities/populateBreadcrumbs.js'
|
||||
|
||||
type ResaveArgs = {
|
||||
collection: CollectionConfig
|
||||
doc: JsonObject
|
||||
draft: boolean
|
||||
pluginConfig: NestedDocsPluginConfig
|
||||
req: PayloadRequest
|
||||
}
|
||||
export const resaveChildren =
|
||||
(pluginConfig: NestedDocsPluginConfig): CollectionAfterChangeHook =>
|
||||
async ({ collection, doc, req }) => {
|
||||
if (collection.versions.drafts && doc._status !== 'published') {
|
||||
// If the parent is a draft, don't resave children
|
||||
return
|
||||
}
|
||||
|
||||
const resave = async ({ collection, doc, draft, pluginConfig, req }: ResaveArgs) => {
|
||||
const parentSlug = pluginConfig?.parentFieldSlug || 'parent'
|
||||
const parentSlug = pluginConfig?.parentFieldSlug || 'parent'
|
||||
|
||||
if (draft) {
|
||||
// If the parent is a draft, don't resave children
|
||||
return
|
||||
} else {
|
||||
const initialDraftChildren = await req.payload.find({
|
||||
collection: collection.slug,
|
||||
depth: 0,
|
||||
@@ -74,7 +63,7 @@ const resave = async ({ collection, doc, draft, pluginConfig, req }: ResaveArgs)
|
||||
})
|
||||
})
|
||||
|
||||
if (sortedChildren) {
|
||||
if (sortedChildren.length) {
|
||||
try {
|
||||
for (const child of sortedChildren) {
|
||||
const isDraft = child._status !== 'published'
|
||||
@@ -82,7 +71,14 @@ const resave = async ({ collection, doc, draft, pluginConfig, req }: ResaveArgs)
|
||||
await req.payload.update({
|
||||
id: child.id,
|
||||
collection: collection.slug,
|
||||
data: populateBreadcrumbs(req, pluginConfig, collection, child),
|
||||
data: populateBreadcrumbs({
|
||||
collection,
|
||||
data: child,
|
||||
generateLabel: pluginConfig.generateLabel,
|
||||
generateURL: pluginConfig.generateURL,
|
||||
parentFieldName: pluginConfig.parentFieldSlug,
|
||||
req,
|
||||
}),
|
||||
depth: 0,
|
||||
draft: isDraft,
|
||||
locale: req.locale,
|
||||
@@ -106,19 +102,6 @@ const resave = async ({ collection, doc, draft, pluginConfig, req }: ResaveArgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const resaveChildren =
|
||||
(pluginConfig: NestedDocsPluginConfig, collection: CollectionConfig): CollectionAfterChangeHook =>
|
||||
async ({ doc, req }) => {
|
||||
await resave({
|
||||
collection,
|
||||
doc,
|
||||
draft: doc._status === 'published' ? false : true,
|
||||
pluginConfig,
|
||||
req,
|
||||
})
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CollectionAfterChangeHook, CollectionConfig } from 'payload'
|
||||
import type { CollectionAfterChangeHook } from 'payload'
|
||||
|
||||
import type { Breadcrumb, NestedDocsPluginConfig } from '../types.js'
|
||||
|
||||
@@ -6,8 +6,8 @@ import type { Breadcrumb, NestedDocsPluginConfig } from '../types.js'
|
||||
// so that we can build its breadcrumbs with the newly created document's ID.
|
||||
|
||||
export const resaveSelfAfterCreate =
|
||||
(pluginConfig: NestedDocsPluginConfig, collection: CollectionConfig): CollectionAfterChangeHook =>
|
||||
async ({ doc, operation, req }) => {
|
||||
(pluginConfig: NestedDocsPluginConfig): CollectionAfterChangeHook =>
|
||||
async ({ collection, doc, operation, req }) => {
|
||||
if (operation !== 'create') {
|
||||
return undefined
|
||||
}
|
||||
@@ -16,13 +16,8 @@ export const resaveSelfAfterCreate =
|
||||
const breadcrumbSlug = pluginConfig.breadcrumbsFieldSlug || 'breadcrumbs'
|
||||
const breadcrumbs = doc[breadcrumbSlug] as unknown as Breadcrumb[]
|
||||
|
||||
const updateAsDraft =
|
||||
typeof collection.versions === 'object' &&
|
||||
collection.versions.drafts &&
|
||||
doc._status !== 'published'
|
||||
|
||||
try {
|
||||
await payload.update({
|
||||
await payload.db.updateOne({
|
||||
id: doc.id,
|
||||
collection: collection.slug,
|
||||
data: {
|
||||
@@ -32,9 +27,8 @@ export const resaveSelfAfterCreate =
|
||||
doc: breadcrumbs.length === i + 1 ? doc.id : crumb.doc,
|
||||
})) || [],
|
||||
},
|
||||
depth: 0,
|
||||
draft: updateAsDraft,
|
||||
locale,
|
||||
draft: collection.versions.drafts && doc._status !== 'published',
|
||||
locale: locale || undefined,
|
||||
req,
|
||||
})
|
||||
} catch (err: unknown) {
|
||||
|
||||
@@ -5,10 +5,10 @@ import type { NestedDocsPluginConfig } from './types.js'
|
||||
import { createBreadcrumbsField } from './fields/breadcrumbs.js'
|
||||
import { createParentField } from './fields/parent.js'
|
||||
import { parentFilterOptions } from './fields/parentFilterOptions.js'
|
||||
import { populateBreadcrumbsBeforeChange } from './hooks/populateBreadcrumbsBeforeChange.js'
|
||||
import { resaveChildren } from './hooks/resaveChildren.js'
|
||||
import { resaveSelfAfterCreate } from './hooks/resaveSelfAfterCreate.js'
|
||||
import { getParents } from './utilities/getParents.js'
|
||||
import { populateBreadcrumbs } from './utilities/populateBreadcrumbs.js'
|
||||
|
||||
export { createBreadcrumbsField, createParentField, getParents }
|
||||
|
||||
@@ -53,14 +53,13 @@ export const nestedDocsPlugin =
|
||||
hooks: {
|
||||
...(collection.hooks || {}),
|
||||
afterChange: [
|
||||
resaveChildren(pluginConfig, collection),
|
||||
resaveSelfAfterCreate(pluginConfig, collection),
|
||||
...(collection?.hooks?.afterChange || []),
|
||||
resaveChildren(pluginConfig),
|
||||
resaveSelfAfterCreate(pluginConfig),
|
||||
],
|
||||
beforeChange: [
|
||||
async ({ data, originalDoc, req }) =>
|
||||
populateBreadcrumbs(req, pluginConfig, collection, data, originalDoc),
|
||||
...(collection?.hooks?.beforeChange || []),
|
||||
populateBreadcrumbsBeforeChange(pluginConfig),
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,23 +1,37 @@
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
import type { Breadcrumb, NestedDocsPluginConfig } from '../types.js'
|
||||
import type { Breadcrumb, GenerateLabel, GenerateURL } from '../types.js'
|
||||
|
||||
export const formatBreadcrumb = (
|
||||
pluginConfig: NestedDocsPluginConfig,
|
||||
collection: CollectionConfig,
|
||||
docs: Array<Record<string, unknown>>,
|
||||
): Breadcrumb => {
|
||||
type Args = {
|
||||
/**
|
||||
* Existing breadcrumb, if any, to base the new breadcrumb on.
|
||||
* This ensures that row IDs are maintained across updates, etc.
|
||||
*/
|
||||
breadcrumb?: Breadcrumb
|
||||
collection: SanitizedCollectionConfig
|
||||
docs: Record<string, unknown>[]
|
||||
generateLabel?: GenerateLabel
|
||||
generateURL?: GenerateURL
|
||||
}
|
||||
|
||||
export const formatBreadcrumb = ({
|
||||
breadcrumb,
|
||||
collection,
|
||||
docs,
|
||||
generateLabel,
|
||||
generateURL,
|
||||
}: Args): Breadcrumb => {
|
||||
let url: string | undefined = undefined
|
||||
let label: string
|
||||
|
||||
const lastDoc = docs[docs.length - 1]!
|
||||
|
||||
if (typeof pluginConfig?.generateURL === 'function') {
|
||||
url = pluginConfig.generateURL(docs, lastDoc)
|
||||
if (typeof generateURL === 'function') {
|
||||
url = generateURL(docs, lastDoc)
|
||||
}
|
||||
|
||||
if (typeof pluginConfig?.generateLabel === 'function') {
|
||||
label = pluginConfig.generateLabel(docs, lastDoc)
|
||||
if (typeof generateLabel === 'function') {
|
||||
label = generateLabel(docs, lastDoc)
|
||||
} else {
|
||||
const title = collection.admin?.useAsTitle ? lastDoc[collection.admin.useAsTitle] : ''
|
||||
|
||||
@@ -25,6 +39,7 @@ export const formatBreadcrumb = (
|
||||
}
|
||||
|
||||
return {
|
||||
...(breadcrumb || {}),
|
||||
doc: lastDoc.id as string,
|
||||
label,
|
||||
url,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import type { CollectionConfig, PayloadRequest } from 'payload'
|
||||
import type { CollectionConfig, Document, PayloadRequest } from 'payload'
|
||||
|
||||
import type { NestedDocsPluginConfig } from '../types.js'
|
||||
|
||||
export const getParents = async (
|
||||
req: PayloadRequest,
|
||||
pluginConfig: NestedDocsPluginConfig,
|
||||
pluginConfig: Pick<NestedDocsPluginConfig, 'generateLabel' | 'generateURL' | 'parentFieldSlug'>,
|
||||
collection: CollectionConfig,
|
||||
doc: Record<string, unknown>,
|
||||
docs: Array<Record<string, unknown>> = [],
|
||||
): Promise<Array<Record<string, unknown>>> => {
|
||||
): Promise<Document[]> => {
|
||||
const parentSlug = pluginConfig?.parentFieldSlug || 'parent'
|
||||
const parent = doc[parentSlug]
|
||||
let retrievedParent: null | Record<string, unknown> = null
|
||||
|
||||
@@ -1,42 +1,62 @@
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import type { Data, Document, PayloadRequest, SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
import type { NestedDocsPluginConfig } from '../types.js'
|
||||
import type { GenerateLabel, GenerateURL } from '../types.js'
|
||||
|
||||
import { formatBreadcrumb } from './formatBreadcrumb.js'
|
||||
import { getParents } from './getParents.js'
|
||||
import { getParents as getAllParentDocuments } from './getParents.js'
|
||||
|
||||
export const populateBreadcrumbs = async (
|
||||
req: any,
|
||||
pluginConfig: NestedDocsPluginConfig,
|
||||
collection: CollectionConfig,
|
||||
data: any,
|
||||
originalDoc?: any,
|
||||
): Promise<any> => {
|
||||
type Args = {
|
||||
breadcrumbsFieldName?: string
|
||||
collection: SanitizedCollectionConfig
|
||||
data: Data
|
||||
generateLabel?: GenerateLabel
|
||||
generateURL?: GenerateURL
|
||||
originalDoc?: Document
|
||||
parentFieldName?: string
|
||||
req: PayloadRequest
|
||||
}
|
||||
export const populateBreadcrumbs = async ({
|
||||
breadcrumbsFieldName = 'breadcrumbs',
|
||||
collection,
|
||||
data,
|
||||
generateLabel,
|
||||
generateURL,
|
||||
originalDoc,
|
||||
parentFieldName,
|
||||
req,
|
||||
}: Args): Promise<Data> => {
|
||||
const newData = data
|
||||
const breadcrumbDocs = [
|
||||
...(await getParents(req, pluginConfig, collection, {
|
||||
...originalDoc,
|
||||
...data,
|
||||
})),
|
||||
]
|
||||
|
||||
const currentDocBreadcrumb = {
|
||||
const currentDocument = {
|
||||
...originalDoc,
|
||||
...data,
|
||||
id: originalDoc?.id ?? data?.id,
|
||||
}
|
||||
|
||||
if (originalDoc?.id) {
|
||||
currentDocBreadcrumb.id = originalDoc?.id
|
||||
}
|
||||
|
||||
breadcrumbDocs.push(currentDocBreadcrumb)
|
||||
|
||||
const breadcrumbs = breadcrumbDocs.map((_, i) =>
|
||||
formatBreadcrumb(pluginConfig, collection, breadcrumbDocs.slice(0, i + 1)),
|
||||
const allParentDocuments: Document[] = await getAllParentDocuments(
|
||||
req,
|
||||
{
|
||||
generateLabel,
|
||||
generateURL,
|
||||
parentFieldSlug: parentFieldName,
|
||||
},
|
||||
collection,
|
||||
currentDocument,
|
||||
)
|
||||
|
||||
return {
|
||||
...newData,
|
||||
[pluginConfig?.breadcrumbsFieldSlug || 'breadcrumbs']: breadcrumbs,
|
||||
}
|
||||
allParentDocuments.push(currentDocument)
|
||||
|
||||
const breadcrumbs = allParentDocuments.map((_, i) =>
|
||||
formatBreadcrumb({
|
||||
breadcrumb: currentDocument[breadcrumbsFieldName]?.[i],
|
||||
collection,
|
||||
docs: allParentDocuments.slice(0, i + 1),
|
||||
generateLabel,
|
||||
generateURL,
|
||||
}),
|
||||
)
|
||||
|
||||
newData[breadcrumbsFieldName] = breadcrumbs
|
||||
|
||||
return newData
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-redirects",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "Redirects plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-search",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "Search plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -149,17 +149,23 @@ export const generateReindexHandler =
|
||||
|
||||
await initTransaction(req)
|
||||
|
||||
for (const collection of collections) {
|
||||
try {
|
||||
await deleteIndexes(collection)
|
||||
await reindexCollection(collection)
|
||||
} catch (err) {
|
||||
const message = t('error:unableToReindexCollection', { collection })
|
||||
payload.logger.error({ err, msg: message })
|
||||
try {
|
||||
const promises = collections.map(async (collection) => {
|
||||
try {
|
||||
await deleteIndexes(collection)
|
||||
await reindexCollection(collection)
|
||||
} catch (err) {
|
||||
const message = t('error:unableToReindexCollection', { collection })
|
||||
payload.logger.error({ err, msg: message })
|
||||
|
||||
await killTransaction(req)
|
||||
return Response.json({ message }, { headers, status: 500 })
|
||||
}
|
||||
await killTransaction(req)
|
||||
throw new Error(message)
|
||||
}
|
||||
})
|
||||
|
||||
await Promise.all(promises)
|
||||
} catch (err: any) {
|
||||
return Response.json({ message: err.message }, { headers, status: 500 })
|
||||
}
|
||||
|
||||
const message = t('general:successfullyReindexed', {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-sentry",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "Sentry plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-seo",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "SEO plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-stripe",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "Stripe plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/richtext-lexical",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "The officially supported Lexical richtext adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -85,6 +85,12 @@ export const RscEntryLexicalField: React.FC<
|
||||
if (args.admin?.hideInsertParagraphAtEnd) {
|
||||
admin.hideInsertParagraphAtEnd = true
|
||||
}
|
||||
if (args.admin?.hideAddBlockButton) {
|
||||
admin.hideAddBlockButton = true
|
||||
}
|
||||
if (args.admin?.hideDraggableBlockElement) {
|
||||
admin.hideDraggableBlockElement = true
|
||||
}
|
||||
|
||||
const props: LexicalRichTextFieldProps = {
|
||||
clientFeatures,
|
||||
|
||||
@@ -131,8 +131,12 @@ export const LexicalEditor: React.FC<
|
||||
<React.Fragment>
|
||||
{!isSmallWidthViewport && editor.isEditable() && (
|
||||
<React.Fragment>
|
||||
<DraggableBlockPlugin anchorElem={floatingAnchorElem} />
|
||||
<AddBlockHandlePlugin anchorElem={floatingAnchorElem} />
|
||||
{editorConfig.admin?.hideDraggableBlockElement ? null : (
|
||||
<DraggableBlockPlugin anchorElem={floatingAnchorElem} />
|
||||
)}
|
||||
{editorConfig.admin?.hideAddBlockButton ? null : (
|
||||
<AddBlockHandlePlugin anchorElem={floatingAnchorElem} />
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
{editorConfig.features.plugins?.map((plugin) => {
|
||||
|
||||
@@ -20,6 +20,14 @@ import type { SanitizedServerEditorConfig } from './lexical/config/types.js'
|
||||
import type { InitialLexicalFormState } from './utilities/buildInitialState.js'
|
||||
|
||||
export type LexicalFieldAdminProps = {
|
||||
/**
|
||||
* Controls if the add block button should be hidden. @default false
|
||||
*/
|
||||
hideAddBlockButton?: boolean
|
||||
/**
|
||||
* Controls if the draggable block element should be hidden. @default false
|
||||
*/
|
||||
hideDraggableBlockElement?: boolean
|
||||
/**
|
||||
* Controls if the gutter (padding to the left & gray vertical line) should be hidden. @default false
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/richtext-slate",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "The officially supported Slate richtext adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-azure",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "Payload storage adapter for Azure Blob Storage",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-gcs",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "Payload storage adapter for Google Cloud Storage",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-s3",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "Payload storage adapter for Amazon S3",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-uploadthing",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "Payload storage adapter for uploadthing",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-vercel-blob",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"description": "Payload storage adapter for Vercel Blob Storage",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/translations",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/ui",
|
||||
"version": "3.53.0",
|
||||
"version": "3.54.0",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -15,6 +15,9 @@ const baseClass = 'code-editor'
|
||||
const CodeEditor: React.FC<Props> = (props) => {
|
||||
const { className, maxHeight, minHeight, options, readOnly, ...rest } = props
|
||||
const MIN_HEIGHT = minHeight ?? 56 // equivalent to 3 lines
|
||||
|
||||
// Extract per-model settings to avoid global conflicts
|
||||
const { insertSpaces, tabSize, trimAutoWhitespace, ...editorOptions } = options || {}
|
||||
const paddingFromProps = options?.padding
|
||||
? (options.padding.top || 0) + (options.padding?.bottom || 0)
|
||||
: 0
|
||||
@@ -36,8 +39,9 @@ const CodeEditor: React.FC<Props> = (props) => {
|
||||
className={classes}
|
||||
loading={<ShimmerEffect height={dynamicHeight} />}
|
||||
options={{
|
||||
detectIndentation: true,
|
||||
detectIndentation: false, // use the tabSize on the model, set onMount
|
||||
hideCursorInOverviewRuler: true,
|
||||
insertSpaces: false,
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
@@ -47,9 +51,9 @@ const CodeEditor: React.FC<Props> = (props) => {
|
||||
alwaysConsumeMouseWheel: false,
|
||||
},
|
||||
scrollBeyondLastLine: false,
|
||||
tabSize: 2,
|
||||
trimAutoWhitespace: false,
|
||||
wordWrap: 'on',
|
||||
...options,
|
||||
...editorOptions,
|
||||
}}
|
||||
theme={theme === 'dark' ? 'vs-dark' : 'vs'}
|
||||
{...rest}
|
||||
@@ -64,6 +68,17 @@ const CodeEditor: React.FC<Props> = (props) => {
|
||||
}}
|
||||
onMount={(editor, monaco) => {
|
||||
rest.onMount?.(editor, monaco)
|
||||
|
||||
// Set per-model options to avoid global conflicts
|
||||
const model = editor.getModel()
|
||||
if (model) {
|
||||
model.updateOptions({
|
||||
insertSpaces: insertSpaces ?? true,
|
||||
tabSize: tabSize ?? 4,
|
||||
trimAutoWhitespace: trimAutoWhitespace ?? true,
|
||||
})
|
||||
}
|
||||
|
||||
setDynamicHeight(
|
||||
Math.max(MIN_HEIGHT, editor.getValue().split('\n').length * 18 + 2 + paddingFromProps),
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import type { CodeFieldClientComponent } from 'payload'
|
||||
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
|
||||
import { CodeEditor } from '../../elements/CodeEditor/index.js'
|
||||
import { RenderCustomComponent } from '../../elements/RenderCustomComponent/index.js'
|
||||
@@ -36,6 +36,9 @@ const CodeFieldComponent: CodeFieldClientComponent = (props) => {
|
||||
validate,
|
||||
} = props
|
||||
|
||||
const inputChangeFromRef = React.useRef<'system' | 'user'>('system')
|
||||
const [editorKey, setEditorKey] = useState<string>('')
|
||||
|
||||
const memoizedValidate = useCallback(
|
||||
(value, options) => {
|
||||
if (typeof validate === 'function') {
|
||||
@@ -48,15 +51,47 @@ const CodeFieldComponent: CodeFieldClientComponent = (props) => {
|
||||
const {
|
||||
customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},
|
||||
disabled,
|
||||
initialValue,
|
||||
path,
|
||||
setValue,
|
||||
showError,
|
||||
value,
|
||||
} = useField({
|
||||
} = useField<string>({
|
||||
potentiallyStalePath: pathFromProps,
|
||||
validate: memoizedValidate,
|
||||
})
|
||||
|
||||
const [initialStringValue, setInitialStringValue] = useState<string | undefined>(() =>
|
||||
(value || initialValue) !== undefined ? (value ?? initialValue) : undefined,
|
||||
)
|
||||
|
||||
const handleChange = useCallback(
|
||||
(val) => {
|
||||
if (readOnly || disabled) {
|
||||
return
|
||||
}
|
||||
inputChangeFromRef.current = 'user'
|
||||
|
||||
try {
|
||||
setValue(val ? val : null)
|
||||
} catch (e) {
|
||||
setValue(val ? val : null)
|
||||
}
|
||||
},
|
||||
[readOnly, disabled, setValue],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (inputChangeFromRef.current === 'system') {
|
||||
setInitialStringValue(
|
||||
(value || initialValue) !== undefined ? (value ?? initialValue) : undefined,
|
||||
)
|
||||
setEditorKey(`${path}-${new Date().toString()}`)
|
||||
}
|
||||
|
||||
inputChangeFromRef.current = 'system'
|
||||
}, [initialValue, path, value])
|
||||
|
||||
const styles = useMemo(() => mergeFieldStyles(field), [field])
|
||||
|
||||
return (
|
||||
@@ -86,11 +121,15 @@ const CodeFieldComponent: CodeFieldClientComponent = (props) => {
|
||||
{BeforeInput}
|
||||
<CodeEditor
|
||||
defaultLanguage={prismToMonacoLanguageMap[language] || language}
|
||||
onChange={readOnly || disabled ? () => null : (val) => setValue(val)}
|
||||
key={editorKey}
|
||||
onChange={handleChange}
|
||||
onMount={onMount}
|
||||
options={editorOptions}
|
||||
readOnly={readOnly || disabled}
|
||||
value={(value as string) || ''}
|
||||
value={initialStringValue}
|
||||
wrapperProps={{
|
||||
id: `field-${path?.replace(/\./g, '__')}`,
|
||||
}}
|
||||
/>
|
||||
{AfterInput}
|
||||
</div>
|
||||
|
||||
@@ -33,6 +33,8 @@ const JSONFieldComponent: JSONFieldClientComponent = (props) => {
|
||||
validate,
|
||||
} = props
|
||||
|
||||
const { tabSize = 2 } = editorOptions || {}
|
||||
|
||||
const [jsonError, setJsonError] = useState<string>()
|
||||
const inputChangeFromRef = React.useRef<'system' | 'user'>('system')
|
||||
const [editorKey, setEditorKey] = useState<string>('')
|
||||
@@ -61,7 +63,7 @@ const JSONFieldComponent: JSONFieldClientComponent = (props) => {
|
||||
|
||||
const [initialStringValue, setInitialStringValue] = useState<string | undefined>(() =>
|
||||
(value || initialValue) !== undefined
|
||||
? JSON.stringify(value ?? initialValue, null, 2)
|
||||
? JSON.stringify(value ?? initialValue, null, tabSize)
|
||||
: undefined,
|
||||
)
|
||||
|
||||
@@ -86,10 +88,14 @@ const JSONFieldComponent: JSONFieldClientComponent = (props) => {
|
||||
: `${uri}?${crypto.randomUUID ? crypto.randomUUID() : uuidv4()}`
|
||||
|
||||
editor.setModel(
|
||||
monaco.editor.createModel(JSON.stringify(value, null, 2), 'json', monaco.Uri.parse(newUri)),
|
||||
monaco.editor.createModel(
|
||||
JSON.stringify(value, null, tabSize),
|
||||
'json',
|
||||
monaco.Uri.parse(newUri),
|
||||
),
|
||||
)
|
||||
},
|
||||
[jsonSchema, value],
|
||||
[jsonSchema, tabSize, value],
|
||||
)
|
||||
|
||||
const handleChange = useCallback(
|
||||
@@ -114,14 +120,14 @@ const JSONFieldComponent: JSONFieldClientComponent = (props) => {
|
||||
if (inputChangeFromRef.current === 'system') {
|
||||
setInitialStringValue(
|
||||
(value || initialValue) !== undefined
|
||||
? JSON.stringify(value ?? initialValue, null, 2)
|
||||
? JSON.stringify(value ?? initialValue, null, tabSize)
|
||||
: undefined,
|
||||
)
|
||||
setEditorKey(new Date().toString())
|
||||
setEditorKey(`${path}-${new Date().toString()}`)
|
||||
}
|
||||
|
||||
inputChangeFromRef.current = 'system'
|
||||
}, [initialValue, value])
|
||||
}, [initialValue, path, tabSize, value])
|
||||
|
||||
const styles = useMemo(() => mergeFieldStyles(field), [field])
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ export interface Config {
|
||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||
};
|
||||
db: {
|
||||
defaultIDType: number;
|
||||
defaultIDType: string;
|
||||
};
|
||||
globals: {
|
||||
menu: Menu;
|
||||
@@ -124,7 +124,7 @@ export interface UserAuthOperations {
|
||||
* via the `definition` "posts".
|
||||
*/
|
||||
export interface Post {
|
||||
id: number;
|
||||
id: string;
|
||||
title?: string | null;
|
||||
content?: {
|
||||
root: {
|
||||
@@ -149,7 +149,7 @@ export interface Post {
|
||||
* via the `definition` "media".
|
||||
*/
|
||||
export interface Media {
|
||||
id: number;
|
||||
id: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
url?: string | null;
|
||||
@@ -193,7 +193,7 @@ export interface Media {
|
||||
* via the `definition` "users".
|
||||
*/
|
||||
export interface User {
|
||||
id: number;
|
||||
id: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
email: string;
|
||||
@@ -217,24 +217,24 @@ export interface User {
|
||||
* via the `definition` "payload-locked-documents".
|
||||
*/
|
||||
export interface PayloadLockedDocument {
|
||||
id: number;
|
||||
id: string;
|
||||
document?:
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'media';
|
||||
value: number | Media;
|
||||
value: string | Media;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'users';
|
||||
value: number | User;
|
||||
value: string | User;
|
||||
} | null);
|
||||
globalSlug?: string | null;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: number | User;
|
||||
value: string | User;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
@@ -244,10 +244,10 @@ export interface PayloadLockedDocument {
|
||||
* via the `definition` "payload-preferences".
|
||||
*/
|
||||
export interface PayloadPreference {
|
||||
id: number;
|
||||
id: string;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: number | User;
|
||||
value: string | User;
|
||||
};
|
||||
key?: string | null;
|
||||
value?:
|
||||
@@ -267,7 +267,7 @@ export interface PayloadPreference {
|
||||
* via the `definition` "payload-migrations".
|
||||
*/
|
||||
export interface PayloadMigration {
|
||||
id: number;
|
||||
id: string;
|
||||
name?: string | null;
|
||||
batch?: number | null;
|
||||
updatedAt: string;
|
||||
@@ -393,7 +393,7 @@ export interface PayloadMigrationsSelect<T extends boolean = true> {
|
||||
* via the `definition` "menu".
|
||||
*/
|
||||
export interface Menu {
|
||||
id: number;
|
||||
id: string;
|
||||
globalText?: string | null;
|
||||
updatedAt?: string | null;
|
||||
createdAt?: string | null;
|
||||
|
||||
@@ -76,24 +76,10 @@ export const PostsCollection: CollectionConfig = {
|
||||
name: 'array',
|
||||
type: 'array',
|
||||
admin: {
|
||||
description: 'If there is no value, a default row will be added by a beforeChange hook',
|
||||
components: {
|
||||
RowLabel: './collections/Posts/ArrayRowLabel.js#ArrayRowLabel',
|
||||
},
|
||||
},
|
||||
hooks: {
|
||||
beforeChange: [
|
||||
({ value }) =>
|
||||
!value?.length
|
||||
? [
|
||||
{
|
||||
defaultTextField: 'This is a computed value.',
|
||||
customTextField: 'This is a computed value.',
|
||||
},
|
||||
]
|
||||
: value,
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'customTextField',
|
||||
@@ -111,5 +97,31 @@ export const PostsCollection: CollectionConfig = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'computedArray',
|
||||
type: 'array',
|
||||
admin: {
|
||||
description:
|
||||
'If there is no value, a default row will be added by a beforeChange hook. Otherwise, modifies the rows on save.',
|
||||
},
|
||||
hooks: {
|
||||
beforeChange: [
|
||||
({ value }) =>
|
||||
!value?.length
|
||||
? [
|
||||
{
|
||||
text: 'This is a computed value.',
|
||||
},
|
||||
]
|
||||
: value,
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -312,22 +312,18 @@ test.describe('Form State', () => {
|
||||
|
||||
// Now test array rows, as their merge logic is different
|
||||
|
||||
await page.locator('#field-array #array-row-0').isVisible()
|
||||
await page.locator('#field-computedArray #computedArray-row-0').isVisible()
|
||||
|
||||
await removeArrayRow(page, { fieldName: 'array' })
|
||||
await removeArrayRow(page, { fieldName: 'computedArray' })
|
||||
|
||||
await page.locator('#field-array .array-row-0').isHidden()
|
||||
await page.locator('#field-computedArray #computedArray-row-0').isHidden()
|
||||
|
||||
await saveDocAndAssert(page)
|
||||
|
||||
await expect(page.locator('#field-array #array-row-0')).toBeVisible()
|
||||
await expect(page.locator('#field-computedArray #computedArray-row-0')).toBeVisible()
|
||||
|
||||
await expect(
|
||||
page.locator('#field-array #array-row-0 #field-array__0__customTextField'),
|
||||
).toHaveValue('This is a computed value.')
|
||||
|
||||
await expect(
|
||||
page.locator('#field-array #array-row-0 #field-array__0__defaultTextField'),
|
||||
page.locator('#field-computedArray #computedArray-row-0 #field-computedArray__0__text'),
|
||||
).toHaveValue('This is a computed value.')
|
||||
})
|
||||
|
||||
|
||||
@@ -144,9 +144,6 @@ export interface Post {
|
||||
}
|
||||
)[]
|
||||
| null;
|
||||
/**
|
||||
* If there is no value, a default row will be added by a beforeChange hook
|
||||
*/
|
||||
array?:
|
||||
| {
|
||||
customTextField?: string | null;
|
||||
@@ -154,6 +151,15 @@ export interface Post {
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
/**
|
||||
* If there is no value, a default row will be added by a beforeChange hook. Otherwise, modifies the rows on save.
|
||||
*/
|
||||
computedArray?:
|
||||
| {
|
||||
text?: string | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
@@ -288,6 +294,12 @@ export interface PostsSelect<T extends boolean = true> {
|
||||
defaultTextField?: T;
|
||||
id?: T;
|
||||
};
|
||||
computedArray?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
};
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
|
||||
@@ -179,19 +179,15 @@ describe('@payloadcms/plugin-nested-docs', () => {
|
||||
})
|
||||
.then(({ docs }) => docs[0])
|
||||
|
||||
if (!updatedChild) {
|
||||
return
|
||||
}
|
||||
|
||||
// breadcrumbs should be updated
|
||||
expect(updatedChild.breadcrumbs).toHaveLength(2)
|
||||
expect(updatedChild!.breadcrumbs).toHaveLength(2)
|
||||
|
||||
expect(updatedChild.breadcrumbs?.[0]?.url).toStrictEqual('/parent-updated')
|
||||
expect(updatedChild.breadcrumbs?.[1]?.url).toStrictEqual('/parent-updated/child')
|
||||
expect(updatedChild!.breadcrumbs?.[0]?.url).toStrictEqual('/parent-updated')
|
||||
expect(updatedChild!.breadcrumbs?.[1]?.url).toStrictEqual('/parent-updated/child')
|
||||
|
||||
// no other data should be affected
|
||||
expect(updatedChild.title).toEqual('child doc')
|
||||
expect(updatedChild.slug).toEqual('child')
|
||||
expect(updatedChild!.title).toEqual('child doc')
|
||||
expect(updatedChild!.slug).toEqual('child')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -178,6 +178,13 @@ export interface User {
|
||||
hash?: string | null;
|
||||
loginAttempts?: number | null;
|
||||
lockUntil?: string | null;
|
||||
sessions?:
|
||||
| {
|
||||
id: string;
|
||||
createdAt?: string | null;
|
||||
expiresAt: string;
|
||||
}[]
|
||||
| null;
|
||||
password?: string | null;
|
||||
}
|
||||
/**
|
||||
@@ -295,6 +302,13 @@ export interface UsersSelect<T extends boolean = true> {
|
||||
hash?: T;
|
||||
loginAttempts?: T;
|
||||
lockUntil?: T;
|
||||
sessions?:
|
||||
| T
|
||||
| {
|
||||
id?: T;
|
||||
createdAt?: T;
|
||||
expiresAt?: T;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
|
||||
@@ -494,20 +494,22 @@ describe('@payloadcms/plugin-search', () => {
|
||||
})
|
||||
|
||||
it('should reindex whole collections', async () => {
|
||||
await payload.create({
|
||||
collection: pagesSlug,
|
||||
data: {
|
||||
title: 'Test page title',
|
||||
_status: 'published',
|
||||
},
|
||||
})
|
||||
await payload.create({
|
||||
collection: postsSlug,
|
||||
data: {
|
||||
title: 'Test page title',
|
||||
_status: 'published',
|
||||
},
|
||||
})
|
||||
await Promise.all([
|
||||
payload.create({
|
||||
collection: pagesSlug,
|
||||
data: {
|
||||
title: 'Test page title',
|
||||
_status: 'published',
|
||||
},
|
||||
}),
|
||||
payload.create({
|
||||
collection: postsSlug,
|
||||
data: {
|
||||
title: 'Test page title',
|
||||
_status: 'published',
|
||||
},
|
||||
}),
|
||||
])
|
||||
|
||||
await wait(200)
|
||||
|
||||
|
||||
@@ -136,6 +136,13 @@ export interface User {
|
||||
hash?: string | null;
|
||||
loginAttempts?: number | null;
|
||||
lockUntil?: string | null;
|
||||
sessions?:
|
||||
| {
|
||||
id: string;
|
||||
createdAt?: string | null;
|
||||
expiresAt: string;
|
||||
}[]
|
||||
| null;
|
||||
password?: string | null;
|
||||
}
|
||||
/**
|
||||
@@ -300,6 +307,13 @@ export interface UsersSelect<T extends boolean = true> {
|
||||
hash?: T;
|
||||
loginAttempts?: T;
|
||||
lockUntil?: T;
|
||||
sessions?:
|
||||
| T
|
||||
| {
|
||||
id?: T;
|
||||
createdAt?: T;
|
||||
expiresAt?: T;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
|
||||
Reference in New Issue
Block a user