feat!: beta-next (#7620)

This PR makes three major changes to the codebase:

1. [Component Paths](#component-paths)
Instead of importing custom components into your config directly, they
are now defined as file paths and rendered only when needed. That way
the Payload config will be significantly more lightweight, and ensures
that the Payload config is 100% server-only and Node-safe. Related
discussion: https://github.com/payloadcms/payload/discussions/6938

2. [Client Config](#client-config)
Deprecates the component map by merging its logic into the client
config. The main goal of this change is for performance and
simplification. There was no need to deeply iterate over the Payload
config twice, once for the component map, and another for the client
config. Instead, we can do everything in the client config one time.
This has also dramatically simplified the client side prop drilling
through the UI library. Now, all components can share the same client
config which matches the exact shape of their Payload config (with the
exception of non-serializable props and mapped custom components).

3. [Custom client component are no longer
server-rendered](#custom-client-components-are-no-longer-server-rendered)
Previously, custom components would be server-rendered, no matter if
they are server or client components. Now, only server components are
rendered on the server. Client components are automatically detected,
and simply get passed through as `MappedComponent` to be rendered fully
client-side.

## Component Paths

Instead of importing custom components into your config directly, they
are now defined as file paths and rendered only when needed. That way
the Payload config will be significantly more lightweight, and ensures
that the Payload config is 100% server-only and Node-safe. Related
discussion: https://github.com/payloadcms/payload/discussions/6938

In order to reference any custom components in the Payload config, you
now have to specify a string path to the component instead of importing
it.

Old:

```ts
import { MyComponent2} from './MyComponent2.js'

admin: {
  components: {
    Label: MyComponent2
  },
},
```

New:

```ts
admin: {
  components: {
    Label: '/collections/Posts/MyComponent2.js#MyComponent2', // <= has to be a relative path based on a baseDir configured in the Payload config - NOT relative based on the importing file
  },
},
```

### Local API within Next.js routes

Previously, if you used the Payload Local API within Next.js pages, all
the client-side modules are being added to the bundle for that specific
page, even if you only need server-side functionality.

This `/test` route, which uses the Payload local API, was previously 460
kb. It is now down to 91 kb and does not bundle the Payload client-side
admin panel anymore.

All tests done
[here](https://github.com/payloadcms/payload-3.0-demo/tree/feat/path-test)
with beta.67/PR, db-mongodb and default richtext-lexical:

**dev /admin before:**
![CleanShot 2024-07-29 at 22 49
12@2x](https://github.com/user-attachments/assets/4428e766-b368-4bcf-8c18-d0187ab64f3e)

**dev /admin after:**
![CleanShot 2024-07-29 at 22 50
49@2x](https://github.com/user-attachments/assets/f494c848-7247-4b02-a650-a3fab4000de6)

---

**dev /test before:**
![CleanShot 2024-07-29 at 22 56
18@2x](https://github.com/user-attachments/assets/1a7e9500-b859-4761-bf63-abbcdac6f8d6)

**dev /test after:**
![CleanShot 2024-07-29 at 22 47
45@2x](https://github.com/user-attachments/assets/f89aa76d-f2d5-4572-9753-2267f034a45a)

---

**build before:**
![CleanShot 2024-07-29 at 22 57
14@2x](https://github.com/user-attachments/assets/5f8f7281-2a4a-40a5-a788-c30ddcdd51b5)

**build after::**
![CleanShot 2024-07-29 at 22 56
39@2x](https://github.com/user-attachments/assets/ea8772fd-512f-4db0-9a81-4b014715a1b7)

### Usage of the Payload Local API / config outside of Next.js

This will make it a lot easier to use the Payload config / local API in
other, server-side contexts. Previously, you might encounter errors due
to client files (like .scss files) not being allowed to be imported.

## Client Config

Deprecates the component map by merging its logic into the client
config. The main goal of this change is for performance and
simplification. There was no need to deeply iterate over the Payload
config twice, once for the component map, and another for the client
config. Instead, we can do everything in the client config one time.
This has also dramatically simplified the client side prop drilling
through the UI library. Now, all components can share the same client
config which matches the exact shape of their Payload config (with the
exception of non-serializable props and mapped custom components).

This is breaking change. The `useComponentMap` hook no longer exists,
and most component props have changed (for the better):

```ts
const { componentMap } = useComponentMap() // old
const { config } = useConfig() // new
```

The `useConfig` hook has also changed in shape, `config` is now a
property _within_ the context obj:

```ts
const config = useConfig() // old
const { config } = useConfig() // new
```

## Custom Client Components are no longer server rendered

Previously, custom components would be server-rendered, no matter if
they are server or client components. Now, only server components are
rendered on the server. Client components are automatically detected,
and simply get passed through as `MappedComponent` to be rendered fully
client-side.

The benefit of this change:

Custom client components can now receive props. Previously, the only way
for them to receive dynamic props from a parent client component was to
use hooks, e.g. `useFieldProps()`. Now, we do have the option of passing
in props to the custom components directly, if they are client
components. This will be simpler than having to look for the correct
hook.

This makes rendering them on the client a little bit more complex, as
you now have to check if that component is a server component (=>
already has been rendered) or a client component (=> not rendered yet,
has to be rendered here). However, this added complexity has been
alleviated through the easy-to-use `<RenderMappedComponent />` helper.

This helper now also handles rendering arrays of custom components (e.g.
beforeList, beforeLogin ...), which actually makes rendering custom
components easier in some cases.

## Misc improvements

This PR includes misc, breaking changes. For example, we previously
allowed unions between components and config object for the same
property. E.g. for the custom view property, you were allowed to pass in
a custom component or an object with other properties, alongside a
custom component.

Those union types are now gone. You can now either pass an object, or a
component. The previous `{ View: MyViewComponent}` is now `{ View: {
Component: MyViewComponent} }` or `{ View: { Default: { Component:
MyViewComponent} } }`.

This dramatically simplifies the way we read & process those properties,
especially in buildComponentMap. We can now simply check for the
existence of one specific property, which always has to be a component,
instead of running cursed runtime checks on a shared union property
which could contain a component, but could also contain functions or
objects.

![CleanShot 2024-07-29 at 23 07
07@2x](https://github.com/user-attachments/assets/1e75aa4c-7a4c-419f-9070-216bb7b9a5e5)

![CleanShot 2024-07-29 at 23 09
40@2x](https://github.com/user-attachments/assets/b4c96450-6b7e-496c-a4f7-59126bfd0991)

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

---------

Co-authored-by: PatrikKozak <patrik@payloadcms.com>
Co-authored-by: Paul <paul@payloadcms.com>
Co-authored-by: Paul Popus <paul@nouance.io>
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
Co-authored-by: James <james@trbl.design>
This commit is contained in:
Alessio Gravili
2024-08-13 12:54:33 -04:00
committed by GitHub
parent 9cb84c48b9
commit 90b7b20699
874 changed files with 12208 additions and 9403 deletions

View File

@@ -0,0 +1,6 @@
'use client'
import React from 'react'
export const MyAvatar: React.FC = () => {
return <p>Some custom Avatar</p>
}

View File

@@ -1,16 +1,6 @@
'use client'
import React from 'react'
import type { Post } from '../../payload-types.js'
export const MyComponent: React.FC = () => {
const test: Post = {
id: 'string',
createdAt: 'string',
text: 'string',
updatedAt: 'string',
}
console.log({ test })
return <p>hi</p>
return <p>Some custom label</p>
}

View File

@@ -0,0 +1,6 @@
'use client'
import React from 'react'
export const MyComponent2: React.FC = () => {
return <p>Some custom label2</p>
}

View File

@@ -1,7 +1,5 @@
import type { CollectionConfig } from 'payload'
import { BlocksFeature, lexicalEditor } from '@payloadcms/richtext-lexical'
export const postsSlug = 'posts'
export const PostsCollection: CollectionConfig = {
@@ -11,6 +9,12 @@ export const PostsCollection: CollectionConfig = {
},
fields: [
{
admin: {
components: {
Label: '/collections/Posts/MyComponent.js#MyComponent',
},
description: 'This is a description',
},
name: 'text',
type: 'text',
},
@@ -19,26 +23,28 @@ export const PostsCollection: CollectionConfig = {
type: 'richText',
},
{
name: 'richText2',
type: 'richText',
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
BlocksFeature({
blocks: [
{
slug: 'testblock',
fields: [
{
name: 'testfield',
type: 'text',
},
],
},
],
}),
],
}),
name: 'myBlocks',
type: 'blocks',
blocks: [
{
slug: 'test',
fields: [
{
name: 'test',
type: 'text',
},
],
},
{
slug: 'someBlock2',
fields: [
{
name: 'test2',
type: 'text',
},
],
},
],
},
// {
// type: 'row',

View File

@@ -1,3 +1,4 @@
import { BlocksFeature, lexicalEditor } from '@payloadcms/richtext-lexical'
import { fileURLToPath } from 'node:url'
import path from 'path'
@@ -13,8 +14,82 @@ export default buildConfigWithDefaults({
// ...extend config here
collections: [
PostsCollection,
{
slug: 'simple',
fields: [
{
name: 'text',
type: 'text',
},
],
},
// MediaCollection
],
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
avatar: {
Component: '/collections/Posts/MyAvatar.js#MyAvatar',
},
},
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
BlocksFeature({
blocks: [
{
admin: {
components: {
Label: '/collections/Posts/MyComponent2.js#MyComponent2',
},
},
slug: 'test',
fields: [
{
name: 'test',
type: 'text',
},
],
},
{
slug: 'someBlock2',
fields: [
{
name: 'test2',
type: 'text',
},
],
},
],
inlineBlocks: [
{
admin: {
components: {
Label: '/collections/Posts/MyComponent2.js#MyComponent2',
},
},
slug: 'test',
fields: [
{
name: 'test',
type: 'text',
},
],
},
{
slug: 'someBlock2',
fields: [
{
name: 'test2',
type: 'text',
},
],
},
],
}),
],
}),
cors: ['http://localhost:3000', 'http://localhost:3001'],
globals: [
MenuGlobal,

View File

@@ -1,24 +1,28 @@
import type { Payload } from 'payload'
import path from 'path'
import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
import { devUser } from '../credentials.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
import { postsSlug } from './collections/Posts/index.js'
import configPromise from './config.js'
let payload: Payload
let token: string
let restClient: NextRESTClient
const { email, password } = devUser
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
describe('_Community Tests', () => {
// --__--__--__--__--__--__--__--__--__
// Boilerplate test setup/teardown
// --__--__--__--__--__--__--__--__--__
beforeAll(async () => {
const initialized = await initPayloadInt(configPromise)
const initialized = await initPayloadInt(dirname)
;({ payload, restClient } = initialized)
const data = await restClient

View File

@@ -12,6 +12,7 @@ export interface Config {
};
collections: {
posts: Post;
simple: Simple;
users: User;
'payload-preferences': PayloadPreference;
'payload-migrations': PayloadMigration;
@@ -68,25 +69,36 @@ export interface Post {
};
[k: string]: unknown;
} | null;
richText2?: {
root: {
type: string;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number;
version: number;
};
[k: string]: unknown;
} | null;
myBlocks?:
| (
| {
test?: string | null;
id?: string | null;
blockName?: string | null;
blockType: 'test';
}
| {
test2?: string | null;
id?: string | null;
blockName?: string | null;
blockType: 'someBlock2';
}
)[]
| null;
updatedAt: string;
createdAt: string;
_status?: ('draft' | 'published') | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "simple".
*/
export interface Simple {
id: string;
text?: string | null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users".

View File

@@ -1,4 +1,4 @@
import type { CollectionConfig, Field } from 'payload/types'
import type { CollectionConfig, Field } from 'payload'
import { disabledSlug } from '../../shared.js'

View File

@@ -8,7 +8,6 @@ import type { Config, User } from './payload-types.js'
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
import { devUser } from '../credentials.js'
import { TestButton } from './TestButton.js'
import { Disabled } from './collections/Disabled/index.js'
import {
createNotUpdateCollectionSlug,
@@ -63,6 +62,9 @@ export default buildConfigWithDefaults({
admin: {
autoLogin: false,
user: 'users',
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [
{
@@ -482,7 +484,7 @@ export default buildConfigWithDefaults({
admin: {
components: {
elements: {
SaveButton: TestButton,
SaveButton: '/TestButton.js#TestButton',
},
},
},

View File

@@ -6,12 +6,14 @@ import type {
RequiredDataFromCollectionSlug,
} from 'payload'
import path from 'path'
import { Forbidden } from 'payload'
import { fileURLToPath } from 'url'
import type { FullyRestricted, Post } from './payload-types.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
import configPromise, { requestHeaders } from './config.js'
import { requestHeaders } from './config.js'
import {
firstArrayText,
fullyRestrictedSlug,
@@ -26,13 +28,14 @@ import {
} from './shared.js'
let payload: Payload
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
describe('Access Control', () => {
let post1: Post
let restricted: FullyRestricted
beforeAll(async () => {
;({ payload } = await initPayloadInt(configPromise))
;({ payload } = await initPayloadInt(dirname))
})
beforeEach(async () => {

View File

@@ -53,6 +53,7 @@ export interface Config {
export interface UserAuthOperations {
forgotPassword: {
email: string;
password: string;
};
login: {
email: string;
@@ -64,11 +65,13 @@ export interface UserAuthOperations {
};
unlock: {
email: string;
password: string;
};
}
export interface NonAdminUserAuthOperations {
forgotPassword: {
email: string;
password: string;
};
login: {
email: string;
@@ -80,6 +83,7 @@ export interface NonAdminUserAuthOperations {
};
unlock: {
email: string;
password: string;
};
}
/**

View File

@@ -1,2 +1,4 @@
/media
/media-gif
app/(payload)/admin/importMap.js
/app/(payload)/admin/importMap.js

View File

@@ -5,6 +5,8 @@ import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views'
import { importMap } from '../admin/importMap.js'
type Args = {
params: {
segments: string[]
@@ -17,6 +19,7 @@ type Args = {
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams })
const NotFound = ({ params, searchParams }: Args) => NotFoundPage({ config, params, searchParams })
const NotFound = ({ params, searchParams }: Args) =>
NotFoundPage({ config, importMap, params, searchParams })
export default NotFound

View File

@@ -5,6 +5,8 @@ import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
import { importMap } from '../admin/importMap.js'
type Args = {
params: {
segments: string[]
@@ -17,6 +19,7 @@ type Args = {
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams })
const Page = ({ params, searchParams }: Args) => RootPage({ config, params, searchParams })
const Page = ({ params, searchParams }: Args) =>
RootPage({ config, importMap, params, searchParams })
export default Page

View File

@@ -0,0 +1 @@
This is just an empty file to ensure the /admin folder is present in git

View File

@@ -5,12 +5,17 @@ import { RootLayout } from '@payloadcms/next/layouts'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import React from 'react'
import { importMap } from './admin/importMap.js'
import './custom.scss'
type Args = {
children: React.ReactNode
}
const Layout = ({ children }: Args) => <RootLayout config={configPromise}>{children}</RootLayout>
const Layout = ({ children }: Args) => (
<RootLayout config={configPromise} importMap={importMap}>
{children}
</RootLayout>
)
export default Layout

View File

@@ -18,6 +18,9 @@ export default buildConfigWithDefaults({
password: devUser.password,
prefillOnly: true,
},
importMap: {
baseDir: path.resolve(dirname),
},
},
cors: ['http://localhost:3000', 'http://localhost:3001'],
globals: [MenuGlobal],

View File

@@ -1,11 +1,13 @@
import type { Payload } from 'payload'
import path from 'path'
import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
import { devUser } from '../credentials.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
import { postsSlug } from './collections/Posts/index.js'
import configPromise from './config.js'
let payload: Payload
let token: string
@@ -13,12 +15,15 @@ let restClient: NextRESTClient
const { email, password } = devUser
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
describe('Admin (Root) Tests', () => {
// --__--__--__--__--__--__--__--__--__
// Boilerplate test setup/teardown
// --__--__--__--__--__--__--__--__--__
beforeAll(async () => {
const initialized = await initPayloadInt(configPromise)
const initialized = await initPayloadInt(dirname)
;({ payload, restClient } = initialized)
const data = await restClient

View File

@@ -1,11 +1,16 @@
import bundleAnalyzer from '@next/bundle-analyzer'
import withPayload from '../../packages/next/src/withPayload.js'
import path from 'path'
import { fileURLToPath } from 'url'
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
})
const __filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(__filename)
export default withBundleAnalyzer(
withPayload({
eslint: {
@@ -17,6 +22,10 @@ export default withBundleAnalyzer(
images: {
domains: ['localhost'],
},
env: {
PAYLOAD_CORE_DEV: 'true',
ROOT_DIR: path.resolve(dirname),
},
webpack: (webpackConfig) => {
webpackConfig.resolve.extensionAlias = {
'.cjs': ['.cts', '.cjs'],

View File

@@ -30,6 +30,7 @@ export interface Config {
export interface UserAuthOperations {
forgotPassword: {
email: string;
password: string;
};
login: {
email: string;
@@ -41,6 +42,7 @@ export interface UserAuthOperations {
};
unlock: {
email: string;
password: string;
};
}
/**

View File

@@ -3,5 +3,5 @@
import React from 'react'
export const AfterInput: React.FC = () => {
return <label className="after-input">#after-input</label>
return <div className="after-input">#after-input</div>
}

View File

@@ -3,5 +3,5 @@
import React from 'react'
export const BeforeInput: React.FC = () => {
return <label className="before-input">#before-input</label>
return <div className="before-input">#before-input</div>
}

View File

@@ -1,10 +1,12 @@
'use client'
import type { DescriptionComponent } from 'payload'
import type { DescriptionComponent, PayloadClientReactComponent } from 'payload'
import { useFieldProps, useFormFields } from '@payloadcms/ui'
import React from 'react'
export const FieldDescriptionComponent: DescriptionComponent<'text'> = () => {
export const FieldDescriptionComponent: PayloadClientReactComponent<
DescriptionComponent<'text'>
> = () => {
const { path } = useFieldProps()
const field = useFormFields(([fields]) => (fields && fields?.[path]) || null)
const { value } = field || {}

View File

@@ -35,11 +35,12 @@ export const CustomSelect = () => {
return (
<div>
<SelectField
hasMany
name={path}
field={{
name: path,
hasMany: true,
options,
}}
onChange={onChange}
options={options}
path={path}
value={value}
/>
</div>

View File

@@ -4,6 +4,6 @@ import React from 'react'
export const CustomDescription: TextFieldDescriptionComponent = (props) => {
return (
<div id="custom-field-description">{`The max length of this field is: ${props?.maxLength}`}</div>
<div id="custom-field-description">{`Description: the max length of this field is: ${props?.maxLength}`}</div>
)
}

View File

@@ -3,5 +3,7 @@ import type { TextFieldLabelComponent } from 'payload'
import React from 'react'
export const CustomLabel: TextFieldLabelComponent = (props) => {
return <div id="custom-field-label">{`The max length of this field is: ${props?.maxLength}`}</div>
return (
<div id="custom-field-label">{`Label: the max length of this field is: ${props?.maxLength}`}</div>
)
}

View File

@@ -1,13 +1,6 @@
import type { CollectionConfig } from 'payload'
import { customFieldsSlug } from '../../slugs.js'
import { AfterInput } from './AfterInput.js'
import { BeforeInput } from './BeforeInput.js'
import { CustomError } from './CustomError.js'
import { FieldDescriptionComponent } from './FieldDescription/index.js'
import { CustomSelect } from './fields/Select/index.js'
import { CustomDescription } from './fields/Text/Description.js'
import { CustomLabel } from './fields/Text/Label.js'
export const CustomFields: CollectionConfig = {
slug: customFieldsSlug,
@@ -17,12 +10,13 @@ export const CustomFields: CollectionConfig = {
type: 'text',
maxLength: 100,
admin: {
placeholder: 'This is a placeholder',
components: {
afterInput: [AfterInput],
beforeInput: [BeforeInput],
Label: CustomLabel,
Description: CustomDescription,
Error: CustomError,
afterInput: ['/collections/CustomFields/AfterInput.js#AfterInput'],
beforeInput: ['/collections/CustomFields/BeforeInput.js#BeforeInput'],
Label: '/collections/CustomFields/fields/Text/Label.js#CustomLabel',
Description: '/collections/CustomFields/fields/Text/Description.js#CustomDescription',
Error: '/collections/CustomFields/CustomError.js#CustomError',
},
},
minLength: 3,
@@ -46,7 +40,8 @@ export const CustomFields: CollectionConfig = {
type: 'text',
admin: {
components: {
Description: FieldDescriptionComponent,
Description:
'/collections/CustomFields/FieldDescription/index.js#FieldDescriptionComponent',
},
},
},
@@ -55,7 +50,7 @@ export const CustomFields: CollectionConfig = {
type: 'text',
admin: {
components: {
Field: CustomSelect,
Field: '/collections/CustomFields/fields/Select/index.js#CustomSelect',
},
},
},

View File

@@ -1,6 +1,5 @@
import type { CollectionConfig } from 'payload'
import { CustomEditView } from '../components/views/CustomEdit/index.js'
import { customViews1CollectionSlug } from '../slugs.js'
export const CustomViews1: CollectionConfig = {
@@ -8,9 +7,13 @@ export const CustomViews1: CollectionConfig = {
admin: {
components: {
views: {
// This will override the entire Edit view including all nested views, i.e. `/edit/:id/*`
// This will override the entire Edit View including all nested views, i.e. `/edit/:id/*`
// To override one specific nested view, use the nested view's slug as the key
Edit: CustomEditView,
edit: {
default: {
Component: '/components/views/CustomEdit/index.js#CustomEditView',
},
},
},
},
},

View File

@@ -1,11 +1,5 @@
import type { CollectionConfig } from 'payload'
import { CustomTabComponent } from '../components/CustomTabComponent/index.js'
import { CustomTabComponentView } from '../components/views/CustomTabComponent/index.js'
import { CustomTabLabelView } from '../components/views/CustomTabLabel/index.js'
import { CustomNestedTabView } from '../components/views/CustomTabNested/index.js'
import { CustomTabWithParamView } from '../components/views/CustomTabWithParam/index.js'
import { CustomVersionsView } from '../components/views/CustomVersions/index.js'
import {
customCollectionParamViewPath,
customCollectionParamViewPathBase,
@@ -21,43 +15,47 @@ export const CustomViews2: CollectionConfig = {
admin: {
components: {
views: {
Edit: {
edit: {
// This will override one specific nested view within the `/edit/:id` route, i.e. `/edit/:id/versions`
CustomViewWithParam: {
Component: CustomTabWithParamView,
Tab: {
customViewWithParam: {
Component: '/components/views/CustomTabWithParam/index.js#CustomTabWithParamView',
tab: {
href: `${customCollectionParamViewPathBase}/123`,
label: 'Custom Param View',
},
path: customCollectionParamViewPath,
},
Default: {
Tab: {
default: {
tab: {
label: customEditLabel,
},
},
MyCustomView: {
Component: CustomTabLabelView,
Tab: {
myCustomView: {
Component: '/components/views/CustomTabLabel/index.js#CustomTabLabelView',
tab: {
href: '/custom-tab-view',
label: customTabLabel,
},
path: '/custom-tab-view',
},
MyCustomViewWithCustomTab: {
Component: CustomTabComponentView,
Tab: CustomTabComponent,
myCustomViewWithCustomTab: {
Component: '/components/views/CustomTabComponent/index.js#CustomTabComponentView',
tab: {
Component: '/components/CustomTabComponent/index.js#CustomTabComponent',
},
path: customTabViewPath,
},
MyCustomViewWithNestedPath: {
Component: CustomNestedTabView,
Tab: {
myCustomViewWithNestedPath: {
Component: '/components/views/CustomTabNested/index.js#CustomNestedTabView',
tab: {
href: customNestedTabViewPath,
label: 'Custom Nested Tab View',
},
path: customNestedTabViewPath,
},
Versions: CustomVersionsView,
versions: {
Component: '/components/views/CustomVersions/index.js#CustomVersionsView',
},
},
},
},

View File

@@ -1,8 +1,5 @@
import type { CollectionConfig } from 'payload'
import { CollectionAPIButton } from '../components/CollectionAPIButton/index.js'
import { CollectionEditButton } from '../components/CollectionEditButton/index.js'
import { CollectionListButton } from '../components/CollectionListButton/index.js'
import { geoCollectionSlug } from '../slugs.js'
export const Geo: CollectionConfig = {
@@ -10,16 +7,16 @@ export const Geo: CollectionConfig = {
admin: {
components: {
views: {
Edit: {
API: {
actions: [CollectionAPIButton],
edit: {
api: {
actions: ['/components/CollectionAPIButton/index.js#CollectionAPIButton'],
},
Default: {
actions: [CollectionEditButton],
default: {
actions: ['/components/CollectionEditButton/index.js#CollectionEditButton'],
},
},
List: {
actions: [CollectionListButton],
list: {
actions: ['/components/CollectionListButton/index.js#CollectionListButton'],
},
},
},

View File

@@ -2,9 +2,6 @@ import type { CollectionConfig } from 'payload'
import { slateEditor } from '@payloadcms/richtext-slate'
import { CustomCell } from '../components/CustomCell/index.js'
import { DemoUIFieldCell } from '../components/DemoUIField/Cell.js'
import { DemoUIField } from '../components/DemoUIField/Field.js'
import { slugPluralLabel, slugSingularLabel } from '../shared.js'
import { postsCollectionSlug } from '../slugs.js'
@@ -57,8 +54,8 @@ export const Posts: CollectionConfig = {
type: 'ui',
admin: {
components: {
Cell: DemoUIFieldCell,
Field: DemoUIField,
Cell: '/components/DemoUIField/Cell.js#DemoUIFieldCell',
Field: '/components/DemoUIField/Field.js#DemoUIField',
},
},
label: 'Demo UI Field',
@@ -91,7 +88,7 @@ export const Posts: CollectionConfig = {
type: 'text',
admin: {
components: {
Cell: CustomCell,
Cell: '/components/CustomCell/index.js#CustomCell',
},
},
},

View File

@@ -1,10 +1,12 @@
import type { SanitizedConfig } from 'payload'
import type { PayloadServerReactComponent, SanitizedConfig } from 'payload'
import React from 'react'
const baseClass = 'admin-button'
export const AdminButton: SanitizedConfig['admin']['components']['actions'][0] = () => {
export const AdminButton: PayloadServerReactComponent<
SanitizedConfig['admin']['components']['actions'][0]
> = () => {
return (
<div
className={baseClass}

View File

@@ -1,4 +1,4 @@
import type { SanitizedConfig } from 'payload'
import type { PayloadServerReactComponent, SanitizedConfig } from 'payload'
import React from 'react'
@@ -6,7 +6,9 @@ import './index.scss'
const baseClass = 'after-dashboard'
export const AfterDashboard: SanitizedConfig['admin']['components']['afterDashboard'][0] = () => {
export const AfterDashboard: PayloadServerReactComponent<
SanitizedConfig['admin']['components']['afterDashboard'][0]
> = () => {
return (
<div className={baseClass}>
<h4>Test Config</h4>

View File

@@ -1,6 +1,6 @@
'use client'
import type { SanitizedConfig } from 'payload'
import type { PayloadClientReactComponent, SanitizedConfig } from 'payload'
import LinkImport from 'next/link.js'
const Link = (LinkImport.default || LinkImport) as unknown as typeof LinkImport.default
@@ -10,9 +10,13 @@ import React from 'react'
const baseClass = 'after-nav-links'
export const AfterNavLinks: SanitizedConfig['admin']['components']['afterNavLinks'][0] = () => {
export const AfterNavLinks: PayloadClientReactComponent<
SanitizedConfig['admin']['components']['afterNavLinks'][0]
> = () => {
const {
routes: { admin: adminRoute },
config: {
routes: { admin: adminRoute },
},
} = useConfig()
return (

View File

@@ -1,11 +1,13 @@
'use client'
import type { SanitizedConfig } from 'payload'
import type { PayloadClientReactComponent, SanitizedConfig } from 'payload'
import { useTranslation } from '@payloadcms/ui'
import React from 'react'
export const BeforeLogin: SanitizedConfig['admin']['components']['beforeLogin'][0] = () => {
export const BeforeLogin: PayloadClientReactComponent<
SanitizedConfig['admin']['components']['beforeLogin'][0]
> = () => {
const translation = useTranslation()
return (

View File

@@ -1,10 +1,10 @@
import type { CustomComponent } from 'payload'
import type { CustomComponent, PayloadServerReactComponent } from 'payload'
import React from 'react'
const baseClass = 'collection-api-button'
export const CollectionAPIButton: CustomComponent = () => {
export const CollectionAPIButton: PayloadServerReactComponent<CustomComponent> = () => {
return (
<div
className={baseClass}

View File

@@ -1,10 +1,10 @@
import type { CustomComponent } from 'payload'
import type { CustomComponent, PayloadServerReactComponent } from 'payload'
import React from 'react'
const baseClass = 'collection-edit-button'
export const CollectionEditButton: CustomComponent = () => {
export const CollectionEditButton: PayloadServerReactComponent<CustomComponent> = () => {
return (
<div
className={baseClass}

View File

@@ -1,10 +1,10 @@
import type { CustomComponent } from 'payload'
import type { CustomComponent, PayloadServerReactComponent } from 'payload'
import React from 'react'
const baseClass = 'collection-list-button'
export const CollectionListButton: CustomComponent = () => {
export const CollectionListButton: PayloadServerReactComponent<CustomComponent> = () => {
return (
<div
className={baseClass}

View File

@@ -8,11 +8,14 @@ import React from 'react'
const Link = (LinkImport.default || LinkImport) as unknown as typeof LinkImport.default
export const CustomTabComponentClient: React.FC<{
path: string
readonly path: string
}> = ({ path }) => {
const {
routes: { admin: adminRoute },
config: {
routes: { admin: adminRoute },
},
} = useConfig()
const params = useParams()
const baseRoute = (params.segments.slice(0, 2) as string[]).join('/')

View File

@@ -4,13 +4,14 @@ import { LogOutIcon, useConfig } from '@payloadcms/ui'
import React from 'react'
export const Logout: React.FC = () => {
const config = useConfig()
const {
admin: {
routes: { logout: logoutRoute },
config: {
admin: {
routes: { logout: logoutRoute },
},
routes: { admin },
},
routes: { admin },
} = config
} = useConfig()
return (
<a href={`${admin}${logoutRoute}#custom`}>

View File

@@ -1,8 +1,8 @@
import type { AdminViewComponent } from 'payload'
import type { AdminViewComponent, PayloadServerReactComponent } from 'payload'
import React, { Fragment } from 'react'
export const CustomAccountView: AdminViewComponent = () => {
export const CustomAccountView: PayloadServerReactComponent<AdminViewComponent> = () => {
return (
<Fragment>
<div

View File

@@ -1,8 +1,8 @@
import type { AdminViewComponent } from 'payload'
import type { AdminViewComponent, PayloadServerReactComponent } from 'payload'
import React, { Fragment } from 'react'
export const CustomDashboardView: AdminViewComponent = () => {
export const CustomDashboardView: PayloadServerReactComponent<AdminViewComponent> = () => {
return (
<Fragment>
<div

View File

@@ -1,10 +1,12 @@
import type { EditViewComponent } from 'payload'
import type { EditViewComponent, PayloadServerReactComponent } from 'payload'
import { SetStepNav } from '@payloadcms/ui'
import { notFound, redirect } from 'next/navigation.js'
import React, { Fragment } from 'react'
export const CustomEditView: EditViewComponent = ({ initPageResult }) => {
export const CustomEditView: PayloadServerReactComponent<EditViewComponent> = ({
initPageResult,
}) => {
if (!initPageResult) {
notFound()
}
@@ -45,7 +47,7 @@ export const CustomEditView: EditViewComponent = ({ initPageResult }) => {
>
<h1>Custom Edit View</h1>
<p>This custom edit view was added through the following Payload config:</p>
<code>components.views.Edit</code>
<code>components.views.edit</code>
<p>
{'This takes precedence over the default edit view, '}
<b>as well as all nested views like versions.</b>

View File

@@ -1,10 +1,12 @@
import type { EditViewComponent } from 'payload'
import type { EditViewComponent, PayloadServerReactComponent } from 'payload'
import { SetStepNav } from '@payloadcms/ui'
import { notFound, redirect } from 'next/navigation.js'
import React, { Fragment } from 'react'
export const CustomDefaultEditView: EditViewComponent = ({ initPageResult }) => {
export const CustomDefaultEditView: PayloadServerReactComponent<EditViewComponent> = ({
initPageResult,
}) => {
if (!initPageResult) {
notFound()
}
@@ -47,7 +49,7 @@ export const CustomDefaultEditView: EditViewComponent = ({ initPageResult }) =>
<p>This custom Default view was added through one of the following Payload configs:</p>
<ul>
<li>
<code>components.views.Edit.Default</code>
<code>components.views.edit.default</code>
<p>
{'This allows you to override only the default edit view specifically, but '}
<b>
@@ -59,7 +61,7 @@ export const CustomDefaultEditView: EditViewComponent = ({ initPageResult }) =>
</p>
</li>
<li>
<code>components.views.Edit.Default.Component</code>
<code>components.views.edit.default.Component</code>
<p>
This is the most granular override, allowing you to override only the Default
component, or any of its other properties like path and label.

View File

@@ -1,4 +1,4 @@
import type { EditViewComponent } from 'payload'
import type { EditViewComponent, PayloadServerReactComponent } from 'payload'
import { SetStepNav } from '@payloadcms/ui'
import { notFound } from 'next/navigation.js'
@@ -6,7 +6,9 @@ import React, { Fragment } from 'react'
import { customTabLabelViewTitle } from '../../../shared.js'
export const CustomTabLabelView: EditViewComponent = ({ initPageResult }) => {
export const CustomTabLabelView: PayloadServerReactComponent<EditViewComponent> = ({
initPageResult,
}) => {
if (!initPageResult) {
notFound()
}

View File

@@ -1,4 +1,4 @@
import type { EditViewComponent } from 'payload'
import type { EditViewComponent, PayloadServerReactComponent } from 'payload'
import { SetStepNav } from '@payloadcms/ui'
import { notFound } from 'next/navigation.js'
@@ -6,7 +6,9 @@ import React, { Fragment } from 'react'
import { customNestedTabViewTitle } from '../../../shared.js'
export const CustomNestedTabView: EditViewComponent = ({ initPageResult }) => {
export const CustomNestedTabView: PayloadServerReactComponent<EditViewComponent> = ({
initPageResult,
}) => {
if (!initPageResult) {
notFound()
}

View File

@@ -1,10 +1,12 @@
import type { EditViewComponent } from 'payload'
import type { EditViewComponent, PayloadServerReactComponent } from 'payload'
import { SetStepNav } from '@payloadcms/ui'
import { notFound, redirect } from 'next/navigation.js'
import React, { Fragment } from 'react'
export const CustomVersionsView: EditViewComponent = ({ initPageResult }) => {
export const CustomVersionsView: PayloadServerReactComponent<EditViewComponent> = ({
initPageResult,
}) => {
if (!initPageResult) {
notFound()
}
@@ -47,7 +49,7 @@ export const CustomVersionsView: EditViewComponent = ({ initPageResult }) => {
<p>This custom Versions view was added through one of the following Payload configs:</p>
<ul>
<li>
<code>components.views.Edit.Versions</code>
<code>components.views.edit.Versions</code>
<p>
{'This allows you to override only the Versions edit view specifically, but '}
<b>
@@ -57,7 +59,7 @@ export const CustomVersionsView: EditViewComponent = ({ initPageResult }) => {
</p>
</li>
<li>
<code>components.views.Edit.Versions.Component</code>
<code>components.views.edit.versions.Component</code>
</li>
<p>
This is the most granular override, allowing you to override only the Versions

View File

@@ -42,9 +42,11 @@ const CustomPassword: React.FC = () => {
return (
<PasswordField
autoComplete="off"
label="Password"
name="password"
required
field={{
name: 'password',
label: 'Password',
required: true,
}}
validate={(value) => {
if (value && confirmValue) {
return confirmValue === value ? true : 'Passwords must match!!!!'

View File

@@ -19,19 +19,6 @@ import { CollectionNoApiView } from './collections/NoApiView.js'
import { Posts } from './collections/Posts.js'
import { UploadCollection } from './collections/Upload.js'
import { Users } from './collections/Users.js'
import { AdminButton } from './components/AdminButton/index.js'
import { AfterDashboard } from './components/AfterDashboard/index.js'
import { AfterNavLinks } from './components/AfterNavLinks/index.js'
import { BeforeLogin } from './components/BeforeLogin/index.js'
import { CustomProvider } from './components/CustomProvider/index.js'
import { Logout } from './components/Logout/index.js'
import { CustomDefaultView } from './components/views/CustomDefault/index.js'
import { CustomMinimalView } from './components/views/CustomMinimal/index.js'
import { CustomView } from './components/views/CustomView/index.js'
import { CustomNestedView } from './components/views/CustomViewNested/index.js'
import { CustomViewWithParam } from './components/views/CustomViewWithParam/index.js'
import { default as customFaviconDark } from './custom-favicon-dark.png'
import { default as customFaviconLight } from './custom-favicon-light.png'
import { CustomGlobalViews1 } from './globals/CustomViews1.js'
import { CustomGlobalViews2 } from './globals/CustomViews2.js'
import { Global } from './globals/Global.js'
@@ -46,42 +33,47 @@ import {
customParamViewPath,
customViewPath,
} from './shared.js'
export default buildConfigWithDefaults({
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
components: {
actions: [AdminButton],
afterDashboard: [AfterDashboard],
afterNavLinks: [AfterNavLinks],
beforeLogin: [BeforeLogin],
actions: ['/components/AdminButton/index.js#AdminButton'],
afterDashboard: ['/components/AfterDashboard/index.js#AfterDashboard'],
afterNavLinks: ['/components/AfterNavLinks/index.js#AfterNavLinks'],
beforeLogin: ['/components/BeforeLogin/index.js#BeforeLogin'],
logout: {
Button: Logout,
Button: '/components/Logout/index.js#Logout',
},
providers: [CustomProvider, CustomProvider],
providers: [
'/components/CustomProvider/index.js#CustomProvider',
'/components/CustomProvider/index.js#CustomProvider',
],
views: {
// Dashboard: CustomDashboardView,
// Account: CustomAccountView,
CustomDefaultView: {
Component: CustomDefaultView,
Component: '/components/views/CustomDefault/index.js#CustomDefaultView',
path: '/custom-default-view',
},
CustomMinimalView: {
Component: CustomMinimalView,
Component: '/components/views/CustomMinimal/index.js#CustomMinimalView',
path: '/custom-minimal-view',
},
CustomNestedView: {
Component: CustomNestedView,
Component: '/components/views/CustomViewNested/index.js#CustomNestedView',
exact: true,
path: customNestedViewPath,
},
CustomView: {
Component: CustomView,
Component: '/components/views/CustomView/index.js#CustomView',
exact: true,
path: customViewPath,
strict: true,
},
CustomViewWithParam: {
Component: CustomViewWithParam,
Component: '/components/views/CustomViewWithParam/index.js#CustomViewWithParam',
path: customParamViewPath,
},
},
@@ -92,13 +84,13 @@ export default buildConfigWithDefaults({
{
type: 'image/png',
rel: 'icon',
url: customFaviconDark.src,
url: '/custom-favicon-dark.png',
},
{
type: 'image/png',
media: '(prefers-color-scheme: dark)',
rel: 'icon',
url: customFaviconLight.src,
url: '/custom-favicon-light.png',
},
],
openGraph: {

View File

@@ -80,7 +80,7 @@ describe('admin1', () => {
let loginURL: string
beforeAll(async ({ browser }, testInfo) => {
const prebuild = Boolean(process.env.CI)
const prebuild = false // Boolean(process.env.CI)
testInfo.setTimeout(TEST_TIMEOUT_LONG)
@@ -148,9 +148,15 @@ describe('admin1', () => {
const favicons = page.locator('link[rel="icon"]')
await expect(favicons).toHaveCount(2)
await expect(favicons.nth(0)).toHaveAttribute('href', /\/custom-favicon-dark\.[a-z\d]+\.png/)
await expect(favicons.nth(0)).toHaveAttribute(
'href',
/\/custom-favicon-dark(\.[a-z\d]+)?\.png/,
)
await expect(favicons.nth(1)).toHaveAttribute('media', '(prefers-color-scheme: dark)')
await expect(favicons.nth(1)).toHaveAttribute('href', /\/custom-favicon-light\.[a-z\d]+\.png/)
await expect(favicons.nth(1)).toHaveAttribute(
'href',
/\/custom-favicon-light(\.[a-z\d]+)?\.png/,
)
})
test('should render custom og:title from root config', async () => {
@@ -559,13 +565,13 @@ describe('admin1', () => {
const prevSibling = await input.evaluateHandle((el) => {
return el.previousElementSibling
})
const prevSiblingText = await page.evaluate((el) => el.textContent, prevSibling)
const prevSiblingText = await page.evaluate((el) => el?.textContent, prevSibling)
expect(prevSiblingText).toEqual('#before-input')
const nextSibling = await input.evaluateHandle((el) => {
return el.nextElementSibling
})
const nextSiblingText = await page.evaluate((el) => el.textContent, nextSibling)
const nextSiblingText = await page.evaluate((el) => el?.textContent, nextSibling)
expect(nextSiblingText).toEqual('#after-input')
})

View File

@@ -47,7 +47,7 @@ describe('admin2', () => {
let adminRoutes: ReturnType<typeof getRoutes>
beforeAll(async ({ browser }, testInfo) => {
const prebuild = Boolean(process.env.CI)
const prebuild = false // Boolean(process.env.CI)
testInfo.setTimeout(TEST_TIMEOUT_LONG)

View File

@@ -1,6 +1,5 @@
import type { GlobalConfig } from 'payload'
import { CustomEditView } from '../components/views/CustomEdit/index.js'
import { customGlobalViews1GlobalSlug } from '../slugs.js'
export const CustomGlobalViews1: GlobalConfig = {
@@ -8,7 +7,11 @@ export const CustomGlobalViews1: GlobalConfig = {
admin: {
components: {
views: {
Edit: CustomEditView,
edit: {
default: {
Component: '/components/views/CustomEdit/index.js#CustomEditView',
},
},
},
},
},

View File

@@ -1,10 +1,5 @@
import type { GlobalConfig } from 'payload'
import { CustomTabComponent } from '../components/CustomTabComponent/index.js'
import { CustomDefaultEditView } from '../components/views/CustomEditDefault/index.js'
import { CustomTabComponentView } from '../components/views/CustomTabComponent/index.js'
import { CustomTabLabelView } from '../components/views/CustomTabLabel/index.js'
import { CustomVersionsView } from '../components/views/CustomVersions/index.js'
import { customGlobalViews2GlobalSlug } from '../slugs.js'
export const CustomGlobalViews2: GlobalConfig = {
@@ -12,22 +7,28 @@ export const CustomGlobalViews2: GlobalConfig = {
admin: {
components: {
views: {
Edit: {
Default: CustomDefaultEditView,
MyCustomView: {
Component: CustomTabLabelView,
Tab: {
edit: {
default: {
Component: '/components/views/CustomEditDefault/index.js#CustomDefaultEditView',
},
myCustomView: {
Component: '/components/views/CustomTabLabel/index.js#CustomTabLabelView',
tab: {
href: '/custom-tab-view',
label: 'Custom',
},
path: '/custom-tab-view',
},
MyCustomViewWithCustomTab: {
Component: CustomTabComponentView,
Tab: CustomTabComponent,
myCustomViewWithCustomTab: {
Component: '/components/views/CustomTabComponent/index.js#CustomTabComponentView',
tab: {
Component: '/components/CustomTabComponent/index.js#CustomTabComponent',
},
path: '/custom-tab-component',
},
Versions: CustomVersionsView,
versions: {
Component: '/components/views/CustomVersions/index.js#CustomVersionsView',
},
},
},
},

View File

@@ -1,7 +1,5 @@
import type { GlobalConfig } from 'payload'
import { GlobalAPIButton } from '../components/GlobalAPIButton/index.js'
import { GlobalEditButton } from '../components/GlobalEditButton/index.js'
import { globalSlug } from '../slugs.js'
export const Global: GlobalConfig = {
@@ -9,12 +7,12 @@ export const Global: GlobalConfig = {
admin: {
components: {
views: {
Edit: {
API: {
actions: [GlobalAPIButton],
edit: {
api: {
actions: ['/components/GlobalAPIButton/index.js#GlobalAPIButton'],
},
Default: {
actions: [GlobalEditButton],
default: {
actions: ['/components/GlobalEditButton/index.js#GlobalEditButton'],
},
},
},

View File

@@ -50,6 +50,7 @@ export interface Config {
export interface UserAuthOperations {
forgotPassword: {
email: string;
password: string;
};
login: {
email: string;
@@ -61,6 +62,7 @@ export interface UserAuthOperations {
};
unlock: {
email: string;
password: string;
};
}
/**

View File

@@ -7,6 +7,11 @@ import { devUser } from '../credentials.js'
import { arraySlug } from './shared.js'
export default buildConfigWithDefaults({
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [
{
slug: arraySlug,

View File

@@ -1,14 +1,19 @@
import type { Payload } from 'payload'
import path from 'path'
import { fileURLToPath } from 'url'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
import configPromise from './config.js'
import { arraySlug } from './shared.js'
let payload: Payload
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
describe('array-update', () => {
beforeAll(async () => {
;({ payload } = await initPayloadInt(configPromise))
;({ payload } = await initPayloadInt(dirname))
})
afterAll(async () => {

View File

@@ -6,7 +6,6 @@ import { v4 as uuid } from 'uuid'
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
import { devUser } from '../credentials.js'
import { AuthDebug } from './AuthDebug.js'
import { apiKeysSlug, namedSaveToJWTValue, saveToJWTKey, slug } from './shared.js'
export default buildConfigWithDefaults({
@@ -16,6 +15,9 @@ export default buildConfigWithDefaults({
password: devUser.password,
prefillOnly: true,
},
importMap: {
baseDir: path.resolve(dirname),
},
user: 'users',
},
collections: [
@@ -165,7 +167,7 @@ export default buildConfigWithDefaults({
type: 'ui',
admin: {
components: {
Field: AuthDebug,
Field: '/AuthDebug.js#AuthDebug',
},
},
label: 'Auth Debug',

View File

@@ -42,6 +42,9 @@ const customAuthenticationStrategy: AuthStrategyFunction = async ({ headers, pay
export default buildConfigWithDefaults({
admin: {
user: 'users',
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [
{

View File

@@ -1,9 +1,11 @@
import type { Payload } from 'payload'
import path from 'path'
import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../../helpers/NextRESTClient.js'
import { initPayloadInt } from '../../helpers/initPayloadInt.js'
import configPromise from './config.js'
import { usersSlug } from './shared.js'
let payload: Payload
@@ -15,9 +17,12 @@ const headers = {
'Content-Type': 'application/json',
}
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
describe('AuthStrategies', () => {
beforeAll(async () => {
;({ payload, restClient } = await initPayloadInt(configPromise))
;({ payload, restClient } = await initPayloadInt(dirname, 'auth/custom-strategy'))
})
afterAll(async () => {

View File

@@ -1,13 +1,14 @@
import type { Payload, User } from 'payload'
import { jwtDecode } from 'jwt-decode'
import path from 'path'
import { fileURLToPath } from 'url'
import { v4 as uuid } from 'uuid'
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
import { devUser } from '../credentials.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
import configPromise from './config.js'
import { apiKeysSlug, namedSaveToJWTValue, saveToJWTKey, slug } from './shared.js'
let restClient: NextRESTClient
@@ -15,9 +16,12 @@ let payload: Payload
const { email, password } = devUser
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
describe('Auth', () => {
beforeAll(async () => {
;({ payload, restClient } = await initPayloadInt(configPromise))
;({ payload, restClient } = await initPayloadInt(dirname))
})
afterAll(async () => {

View File

@@ -1,10 +1,19 @@
import { fileURLToPath } from 'node:url'
import path from 'path'
import { buildConfigWithDefaults } from '../../buildConfigWithDefaults.js'
export const collectionSlug = 'users'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export default buildConfigWithDefaults({
admin: {
user: 'users',
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [
{

View File

@@ -1,17 +1,23 @@
import type { Payload } from 'payload'
import path from 'path'
import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../../helpers/NextRESTClient.js'
import { devUser } from '../../credentials.js'
import { initPayloadInt } from '../../helpers/initPayloadInt.js'
import config, { collectionSlug } from './config.js'
import { collectionSlug } from './config.js'
let restClient: NextRESTClient
let payload: Payload
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
describe('Remove token from auth responses', () => {
beforeAll(async () => {
;({ payload, restClient } = await initPayloadInt(config))
;({ payload, restClient } = await initPayloadInt(dirname, 'auth/removed-token'))
await restClient.POST(`/${collectionSlug}/first-register`, {
body: JSON.stringify({ ...devUser, 'confirm-password': devUser.password }),

View File

@@ -1,8 +1,5 @@
import type { Config, SanitizedConfig } from 'payload'
import { mongooseAdapter } from '@payloadcms/db-mongodb'
import { postgresAdapter } from '@payloadcms/db-postgres'
import { sqliteAdapter } from '@payloadcms/db-sqlite'
import {
AlignFeature,
BlockquoteFeature,
@@ -33,6 +30,7 @@ import { en } from 'payload/i18n/en'
import { es } from 'payload/i18n/es'
import sharp from 'sharp'
import { databaseAdapter } from './databaseAdapter.js'
import { reInitEndpoint } from './helpers/reInit.js'
import { localAPIEndpoint } from './helpers/sdk/endpoint.js'
import { testEmailAdapter } from './testEmailAdapter.js'
@@ -44,52 +42,11 @@ import { testEmailAdapter } from './testEmailAdapter.js'
export async function buildConfigWithDefaults(
testConfig?: Partial<Config>,
options?: {
dbType?: 'mongodb' | 'postgres' | 'sqlite'
disableAutoLogin?: boolean
},
): Promise<SanitizedConfig> {
const databaseAdapters = {
mongodb: mongooseAdapter({
url:
process.env.MONGODB_MEMORY_SERVER_URI ||
process.env.DATABASE_URI ||
'mongodb://127.0.0.1/payloadtests',
collation: {
strength: 1,
},
}),
postgres: postgresAdapter({
pool: {
connectionString: process.env.POSTGRES_URL || 'postgres://127.0.0.1:5432/payloadtests',
},
}),
'postgres-custom-schema': postgresAdapter({
pool: {
connectionString: process.env.POSTGRES_URL || 'postgres://127.0.0.1:5432/payloadtests',
},
schemaName: 'custom',
}),
'postgres-uuid': postgresAdapter({
idType: 'uuid',
pool: {
connectionString: process.env.POSTGRES_URL || 'postgres://127.0.0.1:5432/payloadtests',
},
}),
sqlite: sqliteAdapter({
client: {
url: process.env.SQLITE_URL || 'file:./payloadtests.db',
},
}),
supabase: postgresAdapter({
pool: {
connectionString:
process.env.POSTGRES_URL || 'postgresql://postgres:postgres@127.0.0.1:54322/postgres',
},
}),
}
const config: Config = {
db: databaseAdapters[process.env.PAYLOAD_DATABASE || options?.dbType || 'mongodb'],
db: databaseAdapter,
editor: lexicalEditor({
features: [
ParagraphFeature(),

View File

@@ -40,6 +40,11 @@ export const pointSlug = 'point'
export const errorOnHookSlug = 'error-on-hooks'
export default buildConfigWithDefaults({
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [
{
slug: 'users',

View File

@@ -10,7 +10,7 @@ import type { Post } from './payload-types.js'
import { idToString } from '../helpers/idToString.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
import config, { errorOnHookSlug, pointSlug, relationSlug, slug } from './config.js'
import { errorOnHookSlug, pointSlug, relationSlug, slug } from './config.js'
const title = 'title'
@@ -22,7 +22,7 @@ const dirname = path.dirname(filename)
describe('collections-graphql', () => {
beforeAll(async () => {
;({ payload, restClient } = await initPayloadInt(config))
;({ payload, restClient } = await initPayloadInt(dirname))
// Wait for indexes to be created,
// as we need them to query by point

View File

@@ -40,6 +40,11 @@ export const customIdNumberSlug = 'custom-id-number'
export const errorOnHookSlug = 'error-on-hooks'
export default buildConfigWithDefaults({
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [
{
slug,

View File

@@ -1,13 +1,15 @@
import type { Payload } from 'payload'
import { randomBytes } from 'crypto'
import path from 'path'
import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
import type { Relation } from './config.js'
import type { Post } from './payload-types.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
import config, {
import {
customIdNumberSlug,
customIdSlug,
errorOnHookSlug,
@@ -16,12 +18,15 @@ import config, {
slug,
} from './config.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
let restClient: NextRESTClient
let payload: Payload
describe('collections-rest', () => {
beforeAll(async () => {
;({ payload, restClient } = await initPayloadInt(config))
;({ payload, restClient } = await initPayloadInt(dirname))
// Wait for indexes to be created,
// as we need them to query by point

View File

@@ -6,6 +6,11 @@ import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
import { devUser } from '../credentials.js'
export default buildConfigWithDefaults({
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [
{
slug: 'pages',

View File

@@ -1,16 +1,21 @@
import type { BlockField, Payload } from 'payload'
import path from 'path'
import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
import configPromise from './config.js'
let restClient: NextRESTClient
let payload: Payload
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
describe('Config', () => {
beforeAll(async () => {
;({ payload, restClient } = await initPayloadInt(configPromise))
;({ payload, restClient } = await initPayloadInt(dirname))
})
afterAll(async () => {

View File

@@ -23,6 +23,11 @@ const resolveTransactionId = async (_obj, _args, context) => {
}
export default buildConfigWithDefaults({
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [],
globals: [],
graphQL: {

View File

@@ -1,16 +1,21 @@
import type { Payload } from 'payload'
import path from 'path'
import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
import configPromise from './config.js'
let restClient: NextRESTClient
let payload: Payload
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
describe('Custom GraphQL', () => {
beforeAll(async () => {
;({ payload, restClient } = await initPayloadInt(configPromise))
;({ payload, restClient } = await initPayloadInt(dirname))
})
afterAll(async () => {

View File

@@ -14,6 +14,11 @@ const defaultValueField: TextField = {
}
export default buildConfigWithDefaults({
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [
{
slug: 'posts',

View File

@@ -10,7 +10,6 @@ import { fileURLToPath } from 'url'
import { devUser } from '../credentials.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
import removeFiles from '../helpers/removeFiles.js'
import configPromise from './config.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
@@ -25,7 +24,7 @@ process.env.PAYLOAD_CONFIG_PATH = path.join(dirname, 'config.ts')
describe('database', () => {
beforeAll(async () => {
;({ payload, restClient } = await initPayloadInt(configPromise))
;({ payload, restClient } = await initPayloadInt(dirname))
payload.db.migrationDir = path.join(dirname, './migrations')
const loginResult = await payload.login({

16
test/databaseAdapter.ts Normal file
View File

@@ -0,0 +1,16 @@
// DO NOT MODIFY. This file is automatically generated in initDevAndTest.ts
import { mongooseAdapter } from '@payloadcms/db-mongodb'
export const databaseAdapter = mongooseAdapter({
url:
process.env.MONGODB_MEMORY_SERVER_URI ||
process.env.DATABASE_URI ||
'mongodb://127.0.0.1/payloadtests',
collation: {
strength: 1,
},
})

View File

@@ -10,6 +10,11 @@ import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
import { devUser } from '../credentials.js'
export default buildConfigWithDefaults({
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [
{
slug: 'posts',

View File

@@ -1,18 +1,24 @@
import type { Payload } from 'payload'
import path from 'path'
import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
import { devUser } from '../credentials.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
import configPromise, { postDoc } from './config.js'
import { postDoc } from './config.js'
let restClient: NextRESTClient
let payload: Payload
let token: string
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
describe('dataloader', () => {
beforeAll(async () => {
;({ payload, restClient } = await initPayloadInt(configPromise))
;({ payload, restClient } = await initPayloadInt(dirname))
const loginResult = await payload.login({
collection: 'users',

View File

@@ -1,12 +1,13 @@
import chalk from 'chalk'
import minimist from 'minimist'
import { nextDev } from 'next/dist/cli/next-dev.js'
import open from 'open'
import { getNextJSRootDir } from './helpers/getNextJSRootDir.js'
import { fileURLToPath } from 'node:url'
import path from 'node:path'
import fs from 'node:fs'
import chalk from 'chalk'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import open from 'open'
import { getNextJSRootDir } from './helpers/getNextJSRootDir.js'
import { runInit } from './runInit.js'
import { createTestHooks } from './testHooks.js'
const filename = fileURLToPath(import.meta.url)
@@ -33,6 +34,8 @@ await beforeTest()
const { rootDir, adminRoute } = getNextJSRootDir(testSuiteArg)
await runInit(testSuiteArg, true)
// Open the admin if the -o flag is passed
if (args.o) {
await open(`http://localhost:3000${adminRoute}`)
@@ -42,4 +45,4 @@ if (args.o) {
await nextDev({ port: process.env.PORT || 3000, dirname: rootDir }, 'default', rootDir)
// fetch the admin url to force a render
fetch(`http://localhost:${process.env.PORT || 3000}${adminRoute}`)
void fetch(`http://localhost:${process.env.PORT || 3000}${adminRoute}`)

View File

@@ -8,7 +8,11 @@ import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
import { devUser } from '../credentials.js'
export default buildConfigWithDefaults({
// ...extend config here
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [],
email: nodemailerAdapter(),
onInit: async (payload) => {

View File

@@ -8,7 +8,11 @@ import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
import { devUser } from '../credentials.js'
export default buildConfigWithDefaults({
// ...extend config here
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [],
// NOTE: The from address and api key should be properly set

View File

@@ -13,7 +13,11 @@ const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export default buildConfigWithDefaults({
// ...extend config here
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [PostsCollection, MediaCollection],
email: nodemailerAdapter(),
globals: [MenuGlobal],

View File

@@ -15,6 +15,11 @@ import {
} from './shared.js'
export default buildConfigWithDefaults({
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [
{
slug: collectionSlug,

View File

@@ -1,9 +1,10 @@
import path from 'path'
import { type Payload } from 'payload'
import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
import configPromise from './config.js'
import {
applicationEndpoint,
collectionSlug,
@@ -17,9 +18,12 @@ import {
let payload: Payload
let restClient: NextRESTClient
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
describe('Endpoints', () => {
beforeAll(async () => {
;({ payload, restClient } = await initPayloadInt(configPromise))
;({ payload, restClient } = await initPayloadInt(dirname))
})
afterAll(async () => {

View File

@@ -14,6 +14,11 @@ import { ValidateDraftsOnAndAutosave } from './collections/ValidateDraftsOnAutos
import { GlobalValidateDraftsOn } from './globals/ValidateDraftsOn/index.js'
export default buildConfigWithDefaults({
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [
ErrorFieldsCollection,
Uploads,

View File

@@ -6,6 +6,11 @@ import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
import { devUser } from '../credentials.js'
export default buildConfigWithDefaults({
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [
{
slug: 'blocks-collection',

View File

@@ -4,11 +4,8 @@ const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
import type { CollectionConfig, FilterOptionsProps } from 'payload'
import { withMergedProps } from '@payloadcms/ui/shared'
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
import { devUser } from '../credentials.js'
import { PrePopulateFieldUI } from './PrePopulateFieldUI/index.js'
import {
collection1Slug,
collection2Slug,
@@ -55,6 +52,11 @@ const baseRelationshipFields: CollectionConfig['fields'] = [
]
export default buildConfigWithDefaults({
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [
{
admin: {
@@ -236,13 +238,13 @@ export default buildConfigWithDefaults({
name: 'prePopulate',
admin: {
components: {
Field: withMergedProps({
Component: PrePopulateFieldUI,
toMergeIntoProps: {
Field: {
path: '/PrePopulateFieldUI/index.js#PrePopulateFieldUI',
clientProps: {
hasMultipleRelations: false,
path: 'relationPrePopulate',
},
}),
},
},
width: '25%',
},
@@ -266,13 +268,13 @@ export default buildConfigWithDefaults({
name: 'prePopulateRelationHasMany',
admin: {
components: {
Field: withMergedProps({
Component: PrePopulateFieldUI,
toMergeIntoProps: {
Field: {
path: '/PrePopulateFieldUI/index.js#PrePopulateFieldUI',
clientProps: {
hasMultipleRelations: false,
path: 'relationHasMany',
},
}),
},
},
width: '25%',
},
@@ -296,13 +298,13 @@ export default buildConfigWithDefaults({
name: 'prePopulateToMany',
admin: {
components: {
Field: withMergedProps({
Component: PrePopulateFieldUI,
toMergeIntoProps: {
Field: {
path: '/PrePopulateFieldUI/index.js#PrePopulateFieldUI',
clientProps: {
hasMultipleRelations: true,
path: 'relationToManyHasMany',
},
}),
},
},
width: '25%',
},

View File

@@ -1,21 +1,26 @@
import type { Payload } from 'payload'
import path from 'path'
import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
import type { Collection1 } from './payload-types.js'
import { devUser } from '../credentials.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
import { collection1Slug, versionedRelationshipFieldSlug } from './collectionSlugs.js'
import configPromise from './config.js'
let payload: Payload
let restClient: NextRESTClient
const { email, password } = devUser
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
describe('Relationship Fields', () => {
beforeAll(async () => {
const initialized = await initPayloadInt(configPromise)
const initialized = await initPayloadInt(dirname)
;({ payload, restClient } = initialized)
await restClient.login({

View File

@@ -276,6 +276,7 @@ export interface VersionedRelationshipField {
| null;
updatedAt: string;
createdAt: string;
_status?: ('draft' | 'published') | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema

View File

@@ -1,11 +1,11 @@
'use client'
import type { RowLabelComponent } from 'payload'
import type { PayloadClientReactComponent, RowLabelComponent } from 'payload'
import { useRowLabel } from '@payloadcms/ui'
import React from 'react'
export const ArrayRowLabel: RowLabelComponent = () => {
export const ArrayRowLabel: PayloadClientReactComponent<RowLabelComponent> = () => {
const { data } = useRowLabel<{ title: string }>()
return (
<div style={{ color: 'coral', textTransform: 'uppercase' }}>{data.title || 'Untitled'}</div>

View File

@@ -1,7 +1,6 @@
import type { CollectionConfig } from 'payload'
import { arrayFieldsSlug } from '../../slugs.js'
import { ArrayRowLabel } from './LabelComponent.js'
export const arrayDefaultValue = [{ text: 'row one' }, { text: 'row two' }]
@@ -116,7 +115,7 @@ const ArrayFields: CollectionConfig = {
name: 'rowLabelAsComponent',
admin: {
components: {
RowLabel: ArrayRowLabel,
RowLabel: '/collections/Array/LabelComponent.js#ArrayRowLabel',
},
description: 'Row labels rendered as react components.',
},

View File

@@ -3,7 +3,6 @@ import type { BlockField, CollectionConfig } from 'payload'
import { slateEditor } from '@payloadcms/richtext-slate'
import { blockFieldsSlug, textFieldsSlug } from '../../slugs.js'
import { AddCustomBlocks } from './components/AddCustomBlocks/index.js'
import { getBlocksFieldSeedData } from './shared.js'
export const getBlocksField = (prefix?: string): BlockField => ({
@@ -345,7 +344,7 @@ const BlockFields: CollectionConfig = {
type: 'ui',
admin: {
components: {
Field: AddCustomBlocks,
Field: '/collections/Blocks/components/AddCustomBlocks/index.js#AddCustomBlocks',
},
},
},

View File

@@ -1,6 +1,5 @@
import React from 'react'
import { CustomLabelComponent } from './index.js'
import type { RowLabelComponent } from 'payload'
import type React from 'react'
export const getCustomLabel = ({
fallback,
@@ -10,6 +9,13 @@ export const getCustomLabel = ({
fallback?: string
path: string
style: React.CSSProperties
}) => {
return <CustomLabelComponent fallback={fallback} path={path} style={style} />
}): RowLabelComponent => {
return {
clientProps: {
fallback,
path,
style,
},
path: '/collections/Collapsible/CustomLabel/index.js#CustomLabelComponent',
}
}

View File

@@ -2,7 +2,6 @@ import type { CollectionConfig } from 'payload'
import { collapsibleFieldsSlug } from '../../slugs.js'
import { getCustomLabel } from './CustomLabel/getCustomLabel.js'
import { NestedCustomLabel } from './NestedCustomLabel/index.js'
const CollapsibleFields: CollectionConfig = {
slug: collapsibleFieldsSlug,
@@ -83,12 +82,11 @@ const CollapsibleFields: CollectionConfig = {
description: 'Collapsible label rendered from a function.',
initCollapsed: true,
components: {
RowLabel: () =>
getCustomLabel({
path: 'functionTitleField',
fallback: 'Custom Collapsible Label',
style: {},
}),
RowLabel: getCustomLabel({
path: 'functionTitleField',
fallback: 'Custom Collapsible Label',
style: {},
}),
},
},
fields: [
@@ -103,7 +101,7 @@ const CollapsibleFields: CollectionConfig = {
admin: {
description: 'Collapsible label rendered as a react component.',
components: {
RowLabel: () => getCustomLabel({ path: 'componentTitleField', style: {} }),
RowLabel: getCustomLabel({ path: 'componentTitleField', style: {} }),
},
},
fields: [
@@ -115,8 +113,11 @@ const CollapsibleFields: CollectionConfig = {
type: 'collapsible',
admin: {
components: {
RowLabel: () =>
getCustomLabel({ path: 'nestedTitle', fallback: 'Nested Collapsible', style: {} }),
RowLabel: getCustomLabel({
path: 'nestedTitle',
fallback: 'Nested Collapsible',
style: {},
}),
},
},
fields: [
@@ -135,7 +136,7 @@ const CollapsibleFields: CollectionConfig = {
{
admin: {
components: {
RowLabel: NestedCustomLabel,
RowLabel: '/collections/Collapsible/NestedCustomLabel/index.js#NestedCustomLabel',
},
},
type: 'collapsible',

View File

@@ -3,7 +3,7 @@
import { useField, useFormFields, useFormSubmitted } from '@payloadcms/ui'
import React from 'react'
const CustomError: React.FC<any> = (props) => {
export const CustomError: React.FC<any> = (props) => {
const { path: pathFromProps } = props
const submitted = useFormSubmitted()
const { path } = useField(pathFromProps)
@@ -18,5 +18,3 @@ const CustomError: React.FC<any> = (props) => {
return null
}
export default CustomError

View File

@@ -3,7 +3,7 @@
import { useFieldProps } from '@payloadcms/ui'
import React from 'react'
const CustomLabel = ({ schemaPath }) => {
export const CustomLabel = ({ schemaPath }) => {
const { path: pathFromContext } = useFieldProps()
const path = pathFromContext ?? schemaPath // pathFromContext will be undefined in list view
@@ -14,5 +14,3 @@ const CustomLabel = ({ schemaPath }) => {
</label>
)
}
export default CustomLabel

View File

@@ -1,9 +1,5 @@
import type { CollectionConfig } from 'payload'
import { AfterInput } from './AfterInput.js'
import { BeforeInput } from './BeforeInput.js'
import CustomError from './CustomError.js'
import CustomLabel from './CustomLabel.js'
import { defaultEmail, emailFieldsSlug } from './shared.js'
const EmailFields: CollectionConfig = {
@@ -79,7 +75,7 @@ const EmailFields: CollectionConfig = {
type: 'email',
admin: {
components: {
Label: CustomLabel,
Label: '/collections/Email/CustomLabel.js#CustomLabel',
},
},
},
@@ -88,7 +84,7 @@ const EmailFields: CollectionConfig = {
type: 'email',
admin: {
components: {
Error: CustomError,
Error: '/collections/Email/CustomError.js#CustomError',
},
},
},
@@ -97,8 +93,8 @@ const EmailFields: CollectionConfig = {
type: 'email',
admin: {
components: {
afterInput: [AfterInput],
beforeInput: [BeforeInput],
afterInput: ['/collections/Email/AfterInput.js#AfterInput'],
beforeInput: ['/collections/Email/BeforeInput.js#BeforeInput'],
},
},
},

View File

@@ -140,7 +140,6 @@ const GroupFields: CollectionConfig = {
},
],
},
{
type: 'tabs',
tabs: [

View File

@@ -1,10 +1,12 @@
'use client'
import type { Block } from 'payload'
import type { Block, PayloadClientReactComponent } from 'payload'
import React from 'react'
export const LabelComponent: Block['admin']['components']['Label'] = (props) => {
export const LabelComponent: PayloadClientReactComponent<Block['admin']['components']['Label']> = (
props,
) => {
const { formData } = props
return <div>{formData?.key}</div>
}

View File

@@ -19,7 +19,6 @@ import {
} from '@payloadcms/richtext-lexical'
import { lexicalFieldsSlug } from '../../slugs.js'
import { LabelComponent } from './LabelComponent.js'
import {
ConditionalLayoutBlock,
RadioButtonsBlock,
@@ -87,12 +86,15 @@ const editorConfig: ServerEditorConfig = {
slug: 'myInlineBlock',
admin: {
components: {
Label: LabelComponent,
Label: '/collections/Lexical/LabelComponent.js#LabelComponent',
},
},
fields: [
{
name: 'key',
label: () => {
return 'Key'
},
type: 'select',
options: ['value1', 'value2', 'value3'],
},

View File

@@ -35,7 +35,7 @@ export const LexicalMigrateFields: CollectionConfig = {
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
LexicalPluginToLexicalFeature(),
LexicalPluginToLexicalFeature({ quiet: true }),
TreeViewFeature(),
HTMLConverterFeature(),
LinkFeature({

Some files were not shown because too many files have changed in this diff Show More