Compare commits

..

9 Commits

Author SHA1 Message Date
Elliot DeNolf
46924f6745 chore(release): v3.0.0-beta.59 [skip ci] 2024-07-08 09:44:29 -04:00
Alessio Gravili
1cf7d4db32 feat!: support latest Next.js versions, fix server-only props being passed to Client Document Views (#7026)
**BREAKING:** The minimum required Next.js version has been bumped from
`15.0.0-rc.0` to `15.0.0-canary.53`. This is because the way client
components are represented changed somewhere between those versions, and
it is not feasible to support both versions in our RSC detection logic.
2024-07-08 09:24:56 -04:00
Wilson
f39701401e chore: fix incorrect collection version jsdocs (#7055)
## Description

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Chore (non-breaking change which does not add functionality)

---------

Co-authored-by: Alessio Gravili <alessio@gravili.de>
2024-07-08 05:39:56 +00:00
Paul
b6d85f6efc feat(ui): support nested tabs, groups, collapsibles and rows in where filters in list view (#7044)
Closes https://github.com/payloadcms/payload/issues/7020

Now supports deeply nested tabs, groups, collapsibles and rows in the
where filters in list view including localised labels.

---------

Co-authored-by: Anders Semb Hermansen <anders.hermansen@gmail.com>
2024-07-07 16:57:59 -04:00
Alessio Gravili
187813ef63 feat: avoid unnecessary config await in getPayloadHMR (#7045) 2024-07-06 06:25:48 +00:00
Alessio Gravili
ad5e8444ba chore: upgrade @lexical/eslint-plugin and @types/uuid (#7043) 2024-07-06 04:08:29 +00:00
Alessio Gravili
16c1d949cd fix(richtext-lexical): avoid conflicts between internal component/schema map paths and field names (#7042) 2024-07-05 19:48:22 -04:00
Alessio Gravili
6a3386c3a0 docs: fix typo in storage-adapters.mdx (#7040) 2024-07-05 19:54:51 +00:00
Alessio Gravili
674ef3dc9c chore: clean up packages (#7027) 2024-07-03 17:36:42 -04:00
56 changed files with 451 additions and 454 deletions

View File

@@ -218,7 +218,7 @@ export default buildConfig({
### Installation
```sh
pnpm add @paylaodcms/storage-uploadthing
pnpm add @payloadcms/storage-uploadthing
```
### Usage

View File

@@ -1,6 +1,6 @@
{
"name": "payload-monorepo",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"private": true,
"type": "module",
"scripts": {
@@ -130,7 +130,7 @@
"lint-staged": "^14.0.1",
"minimist": "1.2.8",
"mongodb-memory-server": "^9.0",
"next": "15.0.0-rc.0",
"next": "15.0.0-canary.53",
"open": "^10.1.0",
"p-limit": "^5.0.0",
"pino": "8.15.0",
@@ -152,7 +152,7 @@
"ts-node": "10.9.1",
"tsx": "^4.7.1",
"turbo": "^1.13.3",
"typescript": "5.5.2",
"typescript": "5.5.3",
"uuid": "10.0.0"
},
"peerDependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "create-payload-app",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",
@@ -66,7 +66,6 @@
"@types/esprima": "^4.0.6",
"@types/fs-extra": "^9.0.12",
"@types/jest": "29.5.12",
"@types/node": "20.12.5",
"temp-dir": "2.0.0"
"@types/node": "20.12.5"
}
}

View File

@@ -6,8 +6,8 @@ import { dbReplacements } from './replacements.js'
import { getValidTemplates } from './templates.js'
import globby from 'globby'
import { jest } from '@jest/globals'
import tempDirectory from 'temp-dir'
import fs from 'fs'
import * as os from 'node:os'
describe('createProject', () => {
let projectDir: string
@@ -16,6 +16,7 @@ describe('createProject', () => {
})
beforeEach(() => {
const tempDirectory = fs.realpathSync(os.tmpdir())
projectDir = `${tempDirectory}/${Math.random().toString(36).substring(7)}`
})

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-mongodb",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "The officially supported MongoDB database adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-postgres",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "The officially supported Postgres database adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/email-nodemailer",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "Payload Nodemailer Email Adapter",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/email-resend",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "Payload Resend Email Adapter",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -2,7 +2,7 @@ const baseRules = {
// This rule makes no sense when overriding class methods. This is used a lot in richtext-lexical.
'class-methods-use-this': 'off',
'arrow-body-style': 0,
'import/prefer-default-export': 'off',
'import-x/prefer-default-export': 'off',
'no-restricted-exports': ['warn', { restrictDefaultExports: { direct: true } }],
'no-console': 'warn',
'no-sparse-arrays': 'off',
@@ -10,7 +10,7 @@ const baseRules = {
'no-use-before-define': 'off',
'object-shorthand': 'warn',
'no-useless-escape': 'warn',
'import/no-duplicates': 'warn',
'import-x/no-duplicates': 'warn',
'perfectionist/sort-objects': [
'error',
{
@@ -124,7 +124,7 @@ module.exports = {
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['import'], // Plugins are defined in the overrides to be more specific and only target the files they are meant for.
plugins: ['import-x'], // Plugins are defined in the overrides to be more specific and only target the files they are meant for.
overrides: [
{
files: ['**/*.ts'],
@@ -195,7 +195,7 @@ module.exports = {
],
rules: {}, // Rules are defined in the overrides to be more specific and only target the files they are meant for.
settings: {
'import/parsers': {
'import-x/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
},

View File

@@ -21,11 +21,10 @@
"@typescript-eslint/parser": "7.3.1",
"eslint": "8.57.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-import": "2.25.2",
"eslint-plugin-import-x": "0.5.3",
"eslint-plugin-jest": "27.9.0",
"eslint-plugin-jest-dom": "5.1.0",
"eslint-plugin-jsx-a11y": "6.8.0",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-payload": "workspace:*",
"eslint-plugin-perfectionist": "2.7.0",
"eslint-plugin-react": "7.34.1",

View File

@@ -24,7 +24,6 @@
"eslint-plugin-jest": "27.9.0",
"eslint-plugin-jest-dom": "5.1.0",
"eslint-plugin-jsx-a11y": "6.8.0",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-perfectionist": "2.7.0",
"eslint-plugin-react": "7.34.1",
"eslint-plugin-react-hooks": "4.6.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/graphql",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/live-preview-react",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "The official live preview React SDK for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/live-preview",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "The official live preview JavaScript SDK for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/next",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",
@@ -100,7 +100,7 @@
},
"peerDependencies": {
"graphql": "^16.8.1",
"next": "^15.0.0-rc.0",
"next": "^15.0.0-canary.53",
"payload": "workspace:*"
},
"engines": {

View File

@@ -1,4 +1,4 @@
import type { GeneratedTypes, InitOptions, Payload, SanitizedConfig } from 'payload'
import type { InitOptions, Payload, SanitizedConfig } from 'payload'
import { BasePayload } from 'payload'
import WebSocket from 'ws'
@@ -6,7 +6,7 @@ import WebSocket from 'ws'
let cached: {
payload: Payload | null
promise: Promise<Payload> | null
reload: Promise<boolean> | boolean
reload: Promise<void> | boolean
} = global._payload
if (!cached) {
@@ -53,17 +53,18 @@ export const reload = async (config: SanitizedConfig, payload: Payload): Promise
export const getPayloadHMR = async (options: InitOptions): Promise<Payload> => {
if (!options?.config) {
throw new Error('Error: the payload config is required for getPayload to work.')
throw new Error('Error: the payload config is required for getPayloadHMR to work.')
}
if (cached.payload) {
const config = await options.config // TODO: check if we can move this inside the cached.reload === true condition
if (cached.reload === true) {
let resolve
let resolve: () => void
// getPayloadHMR is called multiple times, in parallel. However, we only want to run `await reload` once. By immediately setting cached.reload to a promise,
// we can ensure that all subsequent calls will wait for the first reload to finish. So if we set it here, the 2nd call of getPayloadHMR
// will reach `if (cached.reload instanceof Promise) {` which then waits for the first reload to finish.
cached.reload = new Promise((res) => (resolve = res))
const config = await options.config
await reload(config, cached.payload)
resolve()
@@ -77,6 +78,7 @@ export const getPayloadHMR = async (options: InitOptions): Promise<Payload> => {
}
if (!cached.promise) {
// no need to await options.config here, as it's already awaited in the BasePayload.init
cached.promise = new BasePayload().init(options)
}

View File

@@ -196,13 +196,6 @@ export const Document: React.FC<AdminViewProps> = async ({
}
}
const viewComponentProps: ServerSideEditViewProps = {
initPageResult,
params,
routeSegments: segments,
searchParams,
}
return (
<DocumentInfoProvider
action={action}
@@ -246,13 +239,14 @@ export const Document: React.FC<AdminViewProps> = async ({
<RenderCustomComponent
CustomComponent={ViewOverride || CustomView}
DefaultComponent={DefaultView}
componentProps={viewComponentProps}
serverOnlyProps={{
i18n,
initPageResult,
locale,
params,
payload,
permissions,
routeSegments: segments,
searchParams,
user,
}}

View File

@@ -1,6 +1,6 @@
{
"name": "payload",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "Node, React, Headless CMS and Application Framework built on Next.js",
"keywords": [
"admin panel",
@@ -104,7 +104,6 @@
"json-schema-to-typescript": "11.0.3",
"jsonwebtoken": "9.0.1",
"minimist": "1.2.8",
"mkdirp": "1.0.4",
"monaco-editor": "0.38.0",
"pino": "8.15.0",
"pino-pretty": "10.2.0",
@@ -123,11 +122,10 @@
"@types/json-schema": "7.0.12",
"@types/jsonwebtoken": "8.5.9",
"@types/minimist": "1.2.2",
"@types/mkdirp": "1.0.2",
"@types/nodemailer": "6.4.14",
"@types/pluralize": "0.0.33",
"@types/react-datepicker": "6.2.0",
"@types/uuid": "^9.0.8",
"@types/uuid": "10.0.0",
"copyfiles": "2.4.1",
"cross-env": "7.0.3",
"esbuild": "^0.21.4",

View File

@@ -459,7 +459,9 @@ export type CollectionConfig<TSlug extends CollectionSlug = any> = {
*/
upload?: UploadConfig | boolean
/**
* Customize the handling of incoming file uploads
* Enable versioning. Set it to true to enable default versions settings,
* or customize versions options by setting the property equal to an object
* containing the version options.
*
* @default false // disable versioning
*/

View File

@@ -2,7 +2,7 @@ import type { OutputInfo, Sharp, SharpOptions } from 'sharp'
import { fileTypeFromBuffer } from 'file-type'
import fs from 'fs'
import mkdirp from 'mkdirp'
import { mkdirSync } from 'node:fs'
import sanitize from 'sanitize-filename'
import type { Collection } from '../collections/config/types.js'
@@ -107,7 +107,7 @@ export const generateFileData = async <T>({
}
if (!disableLocalStorage) {
mkdirp.sync(staticPath)
mkdirSync(staticPath, { recursive: true })
}
let newData = data

View File

@@ -1,47 +1,31 @@
import type React from 'react'
import { isPlainObject } from './isPlainObject.js'
/*
For reference: console.log output of [ClientComponent, RSC] array, tested in Turbo and Webpack (14.3.0-canary.37)
Both component functions async:
Turbo: [ [Function (anonymous)], [AsyncFunction: ExampleServer] ]
Webpack: [ {}, [AsyncFunction: ExampleServer] ]
Both component functions non-async:
Turbo: [ [Function (anonymous)], [Function: ExampleServer] ]
Webpack: [ {}, [Function: ExampleServer] ]
*/
const clientRefSymbol = Symbol.for('react.client.reference')
export function isReactServerComponentOrFunction<T extends any>(
component: React.ComponentType | any,
): component is T {
const isClassComponent =
typeof component === 'function' &&
component.prototype &&
typeof component.prototype.render === 'function'
const isFunctionalComponent =
typeof component === 'function' && (!component.prototype || !component.prototype.render)
if (component === null || component === undefined) {
return false
}
const hasClientComponentSymbol = component.$$typeof == clientRefSymbol
const isFunctionalComponent = typeof component === 'function'
// Anonymous functions are Client Components in Turbopack. RSCs should have a name
const isAnonymousFunction = typeof component === 'function' && component.name === ''
return (isClassComponent || isFunctionalComponent) && !isAnonymousFunction
const isRSC = isFunctionalComponent && !isAnonymousFunction && !hasClientComponentSymbol
return isRSC
}
export function isReactClientComponent<T extends any>(
component: React.ComponentType | any,
): component is T {
const isClientComponentWebpack = typeof component === 'object' && !isPlainObject(component) // In Webpack, client components are {}
const isClientComponentTurbo = typeof component === 'function' && component.name === '' // Anonymous functions are Client Components in Turbopack
return isClientComponentWebpack || isClientComponentTurbo
if (component === null || component === undefined) {
return false
}
return !isReactServerComponentOrFunction(component) && component.$$typeof == clientRefSymbol
}
export function isReactComponentOrFunction<T extends any>(

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-cloud-storage",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "The official cloud storage plugin for Payload CMS",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-cloud",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "The official Payload Cloud plugin",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-form-builder",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "Form builder plugin for Payload CMS",
"keywords": [
"payload",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-nested-docs",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "The official Nested Docs plugin for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-redirects",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "Redirects plugin for Payload",
"keywords": [
"payload",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-relationship-object-ids",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "A Payload plugin to store all relationship IDs as ObjectIDs",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-search",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "Search plugin for Payload",
"keywords": [
"payload",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-seo",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "SEO plugin for Payload",
"keywords": [
"payload",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-stripe",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "Stripe plugin for Payload",
"keywords": [
"payload",
@@ -63,7 +63,7 @@
"@types/lodash.get": "^4.4.7",
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
"@types/uuid": "^9.0.8",
"@types/uuid": "10.0.0",
"payload": "workspace:*"
},
"peerDependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/richtext-lexical",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "The officially supported Lexical richtext adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {
@@ -50,7 +50,7 @@
"@lexical/rich-text": "0.16.1",
"@lexical/selection": "0.16.1",
"@lexical/utils": "0.16.1",
"@types/uuid": "^9.0.8",
"@types/uuid": "10.0.0",
"bson-objectid": "2.0.4",
"dequal": "2.0.3",
"json-schema": "^0.4.0",
@@ -59,7 +59,7 @@
"uuid": "10.0.0"
},
"devDependencies": {
"@lexical/eslint-plugin": "0.15.0",
"@lexical/eslint-plugin": " 0.16.1",
"@payloadcms/eslint-config": "workspace:*",
"@payloadcms/next": "workspace:*",
"@payloadcms/translations": "workspace:*",

View File

@@ -62,7 +62,7 @@ export const RichTextCell: React.FC<
Object.entries(clientFunctions).forEach(([key, plugin]) => {
if (key.startsWith(`lexicalFeature.${schemaPath}.`)) {
if (!key.includes('.components.')) {
if (!key.includes('.lexical_internal_components.')) {
featureProvidersLocal.push(plugin)
}
featureProvidersAndComponentsLoaded++
@@ -167,7 +167,9 @@ export const RichTextCell: React.FC<
featureProviderComponents.map((featureProvider) => {
// get all components starting with key feature.${FeatureProvider.key}.components.{featureComponentKey}
const featureComponentKeys = Array.from(richTextComponentMap.keys()).filter((key) =>
key.startsWith(`feature.${featureProvider.key}.components.`),
key.startsWith(
`lexical_internal_feature.${featureProvider.key}.lexical_internal_components.`,
),
)
const featureComponents: React.ReactNode[] = featureComponentKeys.map((key) => {

View File

@@ -56,8 +56,8 @@ export const BlockComponent: React.FC<Props> = (props) => {
field: { richTextComponentMap },
} = useEditorConfigContext()
const componentMapRenderedFieldsPath = `feature.blocks.fields.${formData?.blockType}`
const schemaFieldsPath = `${schemaPath}.feature.blocks.${formData?.blockType}`
const componentMapRenderedFieldsPath = `lexical_internal_feature.blocks.fields.${formData?.blockType}`
const schemaFieldsPath = `${schemaPath}.lexical_internal_feature.blocks.${formData?.blockType}`
const reducedBlock: ReducedBlock = (
editorConfig?.resolvedFeatureMap?.get('blocks')
@@ -145,7 +145,7 @@ export const BlockComponent: React.FC<Props> = (props) => {
formData={formData}
formSchema={Array.isArray(fieldMap) ? fieldMap : []}
nodeKey={nodeKey}
path={`${path}.feature.blocks.${formData.blockType}`}
path={`${path}.lexical_internal_feature.blocks.${formData.blockType}`}
reducedBlock={reducedBlock}
schemaPath={schemaFieldsPath}
/>

View File

@@ -13,7 +13,7 @@ const useLexicalFeatureProp = <T,>(featureKey: string, componentKey: string, pro
const schemaPath = schemaPathFromCellProps || schemaPathFromFieldProps // schemaPathFromCellProps needs to have priority, as there can be cells within fields (e.g. list drawers) and the cell schemaPath needs to be used there - not the parent field schemaPath. There cannot be fields within cells.
useAddClientFunction(
`lexicalFeature.${schemaPath}.${featureKey}.components.${componentKey}`,
`lexicalFeature.${schemaPath}.${featureKey}.lexical_internal_components.${componentKey}`,
prop,
)
}

View File

@@ -46,7 +46,9 @@ export const RichTextField: React.FC<
let featureProvidersAndComponentsToLoad = 0 // feature providers and components
for (const featureProvider of featureProviderComponents) {
const featureComponentKeys = Array.from(richTextComponentMap.keys()).filter((key) =>
key.startsWith(`feature.${featureProvider.key}.components.`),
key.startsWith(
`lexical_internal_feature.${featureProvider.key}.lexical_internal_components.`,
),
)
featureProvidersAndComponentsToLoad += 1
@@ -60,9 +62,10 @@ export const RichTextField: React.FC<
Object.entries(clientFunctions).forEach(([key, plugin]) => {
if (key.startsWith(`lexicalFeature.${schemaPath}.`)) {
if (!key.includes('.components.')) {
if (!key.includes('.lexical_internal_components.')) {
featureProvidersLocal.push(plugin)
}
featureProvidersAndComponentsLoaded++
}
})
@@ -112,7 +115,9 @@ export const RichTextField: React.FC<
featureProviderComponents.map((featureProvider) => {
// get all components starting with key feature.${FeatureProvider.key}.components.{featureComponentKey}
const featureComponentKeys = Array.from(richTextComponentMap.keys()).filter((key) =>
key.startsWith(`feature.${featureProvider.key}.components.`),
key.startsWith(
`lexical_internal_feature.${featureProvider.key}.lexical_internal_components.`,
),
)
const featureComponents: React.ReactNode[] = featureComponentKeys.map((key) => {
return richTextComponentMap.get(key)

View File

@@ -34,8 +34,8 @@ export const DrawerContent: React.FC<Omit<FieldsDrawerProps, 'drawerSlug' | 'dra
field: { richTextComponentMap },
} = useEditorConfigContext()
const componentMapRenderedFieldsPath = `feature.${featureKey}.fields${schemaPathSuffix ? `.${schemaPathSuffix}` : ''}`
const schemaFieldsPath = `${schemaPath}.feature.${featureKey}${schemaPathSuffix ? `.${schemaPathSuffix}` : ''}`
const componentMapRenderedFieldsPath = `lexical_internal_feature.${featureKey}.fields${schemaPathSuffix ? `.${schemaPathSuffix}` : ''}`
const schemaFieldsPath = `${schemaPath}.lexical_internal_feature.${featureKey}${schemaPathSuffix ? `.${schemaPathSuffix}` : ''}`
const fieldMap = richTextComponentMap.get(componentMapRenderedFieldsPath) // Field Schema

View File

@@ -44,7 +44,7 @@ export const getGenerateComponentMap =
if (Component) {
componentMap.set(
`feature.${featureKey}.components.${componentKey}`,
`lexical_internal_feature.${featureKey}.lexical_internal_components.${componentKey}`,
<WithServerSideProps
Component={Component}
componentKey={componentKey}
@@ -79,11 +79,14 @@ export const getGenerateComponentMap =
disableAddingID: true,
fieldSchema: fields,
i18n,
parentPath: `${schemaPath}.feature.${featureKey}.fields.${schemaKey}`,
parentPath: `${schemaPath}.lexical_internal_feature.${featureKey}.fields.${schemaKey}`,
readOnly: false,
})
componentMap.set(`feature.${featureKey}.fields.${schemaKey}`, mappedFields)
componentMap.set(
`lexical_internal_feature.${featureKey}.fields.${schemaKey}`,
mappedFields,
)
}
}
}

View File

@@ -33,7 +33,7 @@ export const getGenerateSchemaMap =
schemaPath: schemaKey,
})
schemaMap.set(`${schemaPath}.feature.${featureKey}.${schemaKey}`, fields)
schemaMap.set(`${schemaPath}.lexical_internal_feature.${featureKey}.${schemaKey}`, fields)
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/richtext-slate",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "The officially supported Slate richtext adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/storage-azure",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "Payload storage adapter for Azure Blob Storage",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/storage-gcs",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "Payload storage adapter for Google Cloud Storage",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/storage-s3",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "Payload storage adapter for Amazon S3",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/storage-uploadthing",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "Payload storage adapter for uploadthing",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/storage-vercel-blob",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"description": "Payload storage adapter for Vercel Blob Storage",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/translations",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",
@@ -47,7 +47,7 @@
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
"dotenv": "16.4.5",
"prettier": "^3.0.3",
"typescript": "5.5.2"
"typescript": "5.5.3"
},
"publishConfig": {
"exports": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/ui",
"version": "3.0.0-beta.58",
"version": "3.0.0-beta.59",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",
@@ -108,7 +108,7 @@
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-datepicker": "6.2.0",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
"@types/uuid": "^9.0.8",
"@types/uuid": "10.0.0",
"babel-plugin-react-compiler": "0.0.0-experimental-592953e-20240517",
"css-loader": "^6.10.0",
"esbuild": "^0.21.4",
@@ -122,7 +122,7 @@
"turbopack": "^0.0.1"
},
"peerDependencies": {
"next": "^15.0.0-rc.0",
"next": "^15.0.0-canary.53",
"payload": "workspace:*",
"react": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522",
"react-dom": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"

View File

@@ -30,11 +30,11 @@ export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
const { i18n, t } = useTranslation()
const { code: currentLocale } = useLocale()
const [reducedFields, setReducedColumns] = useState(() => reduceFieldMap(fieldMap, i18n))
const [reducedFields, setReducedColumns] = useState(() => reduceFieldMap({ fieldMap, i18n }))
useEffect(() => {
setReducedColumns(reduceFieldMap(fieldMap, i18n, undefined, undefined, currentLocale))
}, [fieldMap, i18n, currentLocale])
setReducedColumns(reduceFieldMap({ fieldMap, i18n }))
}, [fieldMap, i18n])
const { searchParams } = useSearchParams()
const { handleWhereChange } = useListQuery()

View File

@@ -1,38 +1,113 @@
'use client'
import type { ClientTranslationKeys, I18nClient } from '@payloadcms/translations'
import { getTranslation } from '@payloadcms/translations';
import type { FieldMap } from '../../utilities/buildComponentMap.js'
import { createNestedClientFieldPath } from '../../forms/Form/createNestedFieldPath.js'
import { combineLabel } from '../FieldSelect/index.js'
import fieldTypes from './field-types.js'
export const reduceFieldMap = (
fieldMap: FieldMap,
i18n,
labelPrefix?: string,
pathPrefix?: string,
locale?: string,
) => {
export type ReduceFieldMapArgs = {
fieldMap: FieldMap
i18n: I18nClient
labelPrefix?: string
pathPrefix?: string
}
/**
* Reduces a field map to a flat array of fields with labels and values.
* Used in the WhereBuilder component to render the fields in the dropdown.
*/
export const reduceFieldMap = ({ fieldMap, i18n, labelPrefix, pathPrefix }: ReduceFieldMapArgs) => {
return fieldMap.reduce((reduced, field) => {
if (field.disableListFilter) return reduced
if (field.type === 'tabs' && 'tabs' in field.fieldComponentProps) {
const tabs = field.fieldComponentProps.tabs
tabs.forEach((tab) => {
if (tab.name && typeof tab.label === 'string' && tab.fieldMap) {
reduced.push(...reduceFieldMap(tab.fieldMap, i18n, tab.label, tab.name))
if (typeof tab.label !== 'boolean') {
const localizedTabLabel = getTranslation(tab.label, i18n)
const labelWithPrefix = labelPrefix
? labelPrefix + ' > ' + localizedTabLabel
: localizedTabLabel
// Make sure we handle nested tabs
const tabPathPrefix = tab.name
? pathPrefix
? pathPrefix + '.' + tab.name
: tab.name
: pathPrefix
if (typeof localizedTabLabel === 'string') {
reduced.push(
...reduceFieldMap({
fieldMap: tab.fieldMap,
i18n,
labelPrefix: labelWithPrefix,
pathPrefix: tabPathPrefix,
}),
)
}
}
})
return reduced
}
if (field.type === 'group' && 'fieldMap' in field.fieldComponentProps) {
// Rows cant have labels, so we need to handle them differently
if (field.type === 'row' && 'fieldMap' in field.fieldComponentProps) {
reduced.push(
...reduceFieldMap(
field.fieldComponentProps.fieldMap,
...reduceFieldMap({
fieldMap: field.fieldComponentProps.fieldMap,
i18n,
field.fieldComponentProps.label as string,
field.name,
),
labelPrefix,
pathPrefix,
}),
)
return reduced
}
if (field.type === 'collapsible' && 'fieldMap' in field.fieldComponentProps) {
const localizedTabLabel = getTranslation(field.fieldComponentProps.label, i18n)
const labelWithPrefix = labelPrefix
? labelPrefix + ' > ' + localizedTabLabel
: localizedTabLabel
reduced.push(
...reduceFieldMap({
fieldMap: field.fieldComponentProps.fieldMap,
i18n,
labelPrefix: labelWithPrefix,
pathPrefix,
}),
)
return reduced
}
if (field.type === 'group' && 'fieldMap' in field.fieldComponentProps) {
const translatedLabel = getTranslation(field.fieldComponentProps.label, i18n)
const labelWithPrefix = labelPrefix
? translatedLabel
? labelPrefix + ' > ' + translatedLabel
: labelPrefix
: translatedLabel
// Make sure we handle deeply nested groups
const pathWithPrefix = field.name
? pathPrefix
? pathPrefix + '.' + field.name
: field.name
: pathPrefix
reduced.push(
...reduceFieldMap({
fieldMap: field.fieldComponentProps.fieldMap,
i18n,
labelPrefix: labelWithPrefix,
pathPrefix: pathWithPrefix,
}),
)
return reduced
}
@@ -42,18 +117,16 @@ export const reduceFieldMap = (
const operators = fieldTypes[field.type].operators.reduce((acc, operator) => {
if (!operatorKeys.has(operator.value)) {
operatorKeys.add(operator.value)
const operatorKey = `operators:${operator.label}` as ClientTranslationKeys
acc.push({
...operator,
label: i18n.t(`operators:${operator.label}`),
label: i18n.t(operatorKey),
})
}
return acc
}, [])
const localizedLabel =
locale && typeof field.fieldComponentProps.label === 'object'
? field.fieldComponentProps.label[locale]
: field.fieldComponentProps.label
const localizedLabel = getTranslation(field.fieldComponentProps.label, i18n)
const formattedLabel = labelPrefix
? combineLabel({

543
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -20,7 +20,7 @@
"@payloadcms/richtext-lexical": "beta",
"cross-env": "^7.0.3",
"graphql": "^16.8.1",
"next": "15.0.0-rc.0",
"next": "15.0.0-canary.53",
"payload": "beta",
"react": "^19.0.0-rc-f994737d14-20240522",
"react-dom": "^19.0.0-rc-f994737d14-20240522",
@@ -32,7 +32,7 @@
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
"eslint": "^8",
"eslint-config-next": "15.0.0-rc.0",
"typescript": "5.5.2"
"typescript": "5.5.3"
},
"engines": {
"node": "^18.20.2 || >=20.9.0"

View File

@@ -20,7 +20,7 @@
"@payloadcms/richtext-lexical": "beta",
"cross-env": "^7.0.3",
"graphql": "^16.8.1",
"next": "15.0.0-rc.0",
"next": "15.0.0-canary.53",
"payload": "beta",
"react": "^19.0.0-rc-f994737d14-20240522",
"react-dom": "^19.0.0-rc-f994737d14-20240522",
@@ -32,7 +32,7 @@
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
"eslint": "^8",
"eslint-config-next": "15.0.0-rc.0",
"typescript": "5.5.2"
"typescript": "5.5.3"
},
"engines": {
"node": "^18.20.2 || >=20.9.0"

View File

@@ -20,7 +20,7 @@
"@payloadcms/richtext-lexical": "beta",
"cross-env": "^7.0.3",
"graphql": "^16.8.1",
"next": "15.0.0-rc.0",
"next": "15.0.0-canary.53",
"payload": "beta",
"react": "^19.0.0-rc-f994737d14-20240522",
"react-dom": "^19.0.0-rc-f994737d14-20240522",
@@ -32,7 +32,7 @@
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
"eslint": "^8",
"eslint-config-next": "15.0.0-rc.0",
"typescript": "5.5.2"
"typescript": "5.5.3"
},
"engines": {
"node": "^18.20.2 || >=20.9.0"

View File

@@ -21,7 +21,7 @@
"@payloadcms/storage-vercel-blob": "beta",
"cross-env": "^7.0.3",
"graphql": "^16.8.1",
"next": "15.0.0-rc.0",
"next": "15.0.0-canary.53",
"payload": "beta",
"react": "^19.0.0-rc-f994737d14-20240522",
"react-dom": "^19.0.0-rc-f994737d14-20240522"
@@ -32,7 +32,7 @@
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
"eslint": "^8",
"eslint-config-next": "15.0.0-rc.0",
"typescript": "5.5.2"
"typescript": "5.5.3"
},
"engines": {
"node": "^18.20.2 || >=20.9.0"

View File

@@ -21,7 +21,7 @@
"@payloadcms/storage-vercel-blob": "beta",
"cross-env": "^7.0.3",
"graphql": "^16.8.1",
"next": "15.0.0-rc.0",
"next": "15.0.0-canary.53",
"payload": "beta",
"react": "^19.0.0-rc-f994737d14-20240522",
"react-dom": "^19.0.0-rc-f994737d14-20240522"
@@ -32,7 +32,7 @@
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
"eslint": "^8",
"eslint-config-next": "15.0.0-rc.0",
"typescript": "5.5.2"
"typescript": "5.5.3"
},
"engines": {
"node": "^18.20.2 || >=20.9.0"

View File

@@ -738,7 +738,8 @@ describe('admin2', () => {
await page.locator('.where-builder__add-first-filter').click()
await page.locator('.condition__field .rs__control').click()
const options = page.locator('.rs__option')
await expect(options.locator('text=Title')).toHaveText('Title')
await expect(options.locator('text=Tab 1 > Title')).toHaveText('Tab 1 > Title')
// list columns
await expect(page.locator('#heading-title .sort-column__label')).toHaveText('Title')

View File

@@ -62,7 +62,7 @@
"slate": "0.91.4",
"tempy": "^1.0.1",
"ts-essentials": "7.0.3",
"typescript": "5.5.2",
"typescript": "5.5.3",
"uploadthing": "^6.10.1"
},
"overrides": {