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:**  **dev /admin after:**  --- **dev /test before:**  **dev /test after:**  --- **build before:**  **build after::**  ### 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.   - [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:
6
test/_community/collections/Posts/MyAvatar.tsx
Normal file
6
test/_community/collections/Posts/MyAvatar.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
|
||||
export const MyAvatar: React.FC = () => {
|
||||
return <p>Some custom Avatar</p>
|
||||
}
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
6
test/_community/collections/Posts/MyComponent2.tsx
Normal file
6
test/_community/collections/Posts/MyComponent2.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
|
||||
export const MyComponent2: React.FC = () => {
|
||||
return <p>Some custom label2</p>
|
||||
}
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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".
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CollectionConfig, Field } from 'payload/types'
|
||||
import type { CollectionConfig, Field } from 'payload'
|
||||
|
||||
import { disabledSlug } from '../../shared.js'
|
||||
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
/**
|
||||
|
||||
2
test/admin-root/.gitignore
vendored
2
test/admin-root/.gitignore
vendored
@@ -1,2 +1,4 @@
|
||||
/media
|
||||
/media-gif
|
||||
app/(payload)/admin/importMap.js
|
||||
/app/(payload)/admin/importMap.js
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
1
test/admin-root/app/(payload)/admin/ignore
Normal file
1
test/admin-root/app/(payload)/admin/ignore
Normal file
@@ -0,0 +1 @@
|
||||
This is just an empty file to ensure the /admin folder is present in git
|
||||
@@ -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
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
@@ -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 || {}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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'],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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('/')
|
||||
|
||||
@@ -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`}>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!!!!'
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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'],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -42,6 +42,9 @@ const customAuthenticationStrategy: AuthStrategyFunction = async ({ headers, pay
|
||||
export default buildConfigWithDefaults({
|
||||
admin: {
|
||||
user: 'users',
|
||||
importMap: {
|
||||
baseDir: path.resolve(dirname),
|
||||
},
|
||||
},
|
||||
collections: [
|
||||
{
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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: [
|
||||
{
|
||||
|
||||
@@ -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 }),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -23,6 +23,11 @@ const resolveTransactionId = async (_obj, _args, context) => {
|
||||
}
|
||||
|
||||
export default buildConfigWithDefaults({
|
||||
admin: {
|
||||
importMap: {
|
||||
baseDir: path.resolve(dirname),
|
||||
},
|
||||
},
|
||||
collections: [],
|
||||
globals: [],
|
||||
graphQL: {
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -14,6 +14,11 @@ const defaultValueField: TextField = {
|
||||
}
|
||||
|
||||
export default buildConfigWithDefaults({
|
||||
admin: {
|
||||
importMap: {
|
||||
baseDir: path.resolve(dirname),
|
||||
},
|
||||
},
|
||||
collections: [
|
||||
{
|
||||
slug: 'posts',
|
||||
|
||||
@@ -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
16
test/databaseAdapter.ts
Normal 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,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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}`)
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -15,6 +15,11 @@ import {
|
||||
} from './shared.js'
|
||||
|
||||
export default buildConfigWithDefaults({
|
||||
admin: {
|
||||
importMap: {
|
||||
baseDir: path.resolve(dirname),
|
||||
},
|
||||
},
|
||||
collections: [
|
||||
{
|
||||
slug: collectionSlug,
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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%',
|
||||
},
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.',
|
||||
},
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -140,7 +140,6 @@ const GroupFields: CollectionConfig = {
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
type: 'tabs',
|
||||
tabs: [
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
@@ -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'],
|
||||
},
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user