fix: remove unsupported path property from default document view configs (#12774)
Customizing the `path` property on default document views is currently
not supported, but the types suggest that it is. You can only provide a
path to custom views. This PR ensures that `path` cannot be set on
default views as expected.
For example:
```ts
import type { CollectionConfig } from 'payload'
export const MyCollectionConfig: CollectionConfig = {
// ...
admin: {
components: {
views: {
edit: {
default: {
path: '/' // THIS IS NOT ALLOWED!
},
myCustomView: {
path: '/edit', // THIS IS ALLOWED!
Component: '/collections/CustomViews3/MyEditView.js#MyEditView',
},
},
},
},
},
}
```
For background context, this was deeply explored in #12701. This is not
planned, however, due to [performance and maintainability
concerns](https://github.com/payloadcms/payload/pull/12701#issuecomment-2963926925),
plus [there are alternatives to achieve
this](https://github.com/payloadcms/payload/pull/12772).
This PR also fixes and improves various jsdocs, and fixes a typo found
in the docs.
This commit is contained in:
@@ -88,7 +88,7 @@ export const MyCollection: CollectionConfig = {
|
||||
|
||||
### Edit View
|
||||
|
||||
The Edit View is where users interact with individual Collection and Global Documents. This is where they can view, edit, and save their content. the Edit View is keyed under the `default` property in the `views.edit` object.
|
||||
The Edit View is where users interact with individual Collection and Global Documents. This is where they can view, edit, and save their content. The Edit View is keyed under the `default` property in the `views.edit` object.
|
||||
|
||||
For more information on customizing the Edit View, see the [Edit View](./edit-view) documentation.
|
||||
|
||||
@@ -107,8 +107,8 @@ export const MyCollection: CollectionConfig = {
|
||||
components: {
|
||||
views: {
|
||||
edit: {
|
||||
myCustomTab: {
|
||||
Component: '/path/to/MyCustomTab',
|
||||
myCustomView: {
|
||||
Component: '/path/to/MyCustomView',
|
||||
path: '/my-custom-tab',
|
||||
// highlight-start
|
||||
tab: {
|
||||
@@ -116,7 +116,7 @@ export const MyCollection: CollectionConfig = {
|
||||
},
|
||||
// highlight-end
|
||||
},
|
||||
anotherCustomTab: {
|
||||
anotherCustomView: {
|
||||
Component: '/path/to/AnotherCustomView',
|
||||
path: '/another-custom-view',
|
||||
// highlight-start
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import type { EditViewConfig, SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload'
|
||||
import type { DocumentViewConfig, SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload'
|
||||
|
||||
import { documentViewKeys } from './tabs/index.js'
|
||||
|
||||
export const getCustomViews = (args: {
|
||||
collectionConfig: SanitizedCollectionConfig
|
||||
globalConfig: SanitizedGlobalConfig
|
||||
}): EditViewConfig[] => {
|
||||
}): DocumentViewConfig[] => {
|
||||
const { collectionConfig, globalConfig } = args
|
||||
|
||||
let customViews: EditViewConfig[]
|
||||
let customViews: DocumentViewConfig[]
|
||||
|
||||
if (collectionConfig) {
|
||||
const collectionViewsConfig =
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { EditViewConfig, SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload'
|
||||
import type { DocumentViewConfig, SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload'
|
||||
|
||||
export const getViewConfig = (args: {
|
||||
collectionConfig: SanitizedCollectionConfig
|
||||
globalConfig: SanitizedGlobalConfig
|
||||
name: string
|
||||
}): EditViewConfig => {
|
||||
}): DocumentViewConfig => {
|
||||
const { name, collectionConfig, globalConfig } = args
|
||||
|
||||
if (collectionConfig) {
|
||||
|
||||
@@ -98,9 +98,11 @@ export const DocumentTabs: React.FC<{
|
||||
|
||||
return null
|
||||
})}
|
||||
{customViews?.map((CustomView, index) => {
|
||||
if ('tab' in CustomView) {
|
||||
const { path, tab } = CustomView
|
||||
{customViews?.map((customViewConfig, index) => {
|
||||
if ('tab' in customViewConfig) {
|
||||
const { tab } = customViewConfig
|
||||
|
||||
const path = 'path' in customViewConfig ? customViewConfig.path : ''
|
||||
|
||||
if (tab.Component) {
|
||||
return RenderServerComponent({
|
||||
|
||||
@@ -10,7 +10,7 @@ import { generateLivePreviewViewMetadata } from '../LivePreview/metadata.js'
|
||||
import { generateNotFoundViewMetadata } from '../NotFound/metadata.js'
|
||||
import { generateVersionViewMetadata } from '../Version/metadata.js'
|
||||
import { generateVersionsViewMetadata } from '../Versions/metadata.js'
|
||||
import { getViewsFromConfig } from './getViewsFromConfig.js'
|
||||
import { getViewsFromConfig } from './getDocumentView.js'
|
||||
|
||||
export type GenerateEditViewMetadata = (
|
||||
args: {
|
||||
|
||||
@@ -16,18 +16,18 @@ import { logError } from 'payload'
|
||||
import { formatAdminURL } from 'payload/shared'
|
||||
import React from 'react'
|
||||
|
||||
import type { ViewFromConfig } from './getDocumentView.js'
|
||||
import type { GenerateEditViewMetadata } from './getMetaBySegment.js'
|
||||
import type { ViewFromConfig } from './getViewsFromConfig.js'
|
||||
|
||||
import { DocumentHeader } from '../../elements/DocumentHeader/index.js'
|
||||
import { NotFoundView } from '../NotFound/index.js'
|
||||
import { getDocPreferences } from './getDocPreferences.js'
|
||||
import { getDocumentData } from './getDocumentData.js'
|
||||
import { getDocumentPermissions } from './getDocumentPermissions.js'
|
||||
import { getViewsFromConfig } from './getDocumentView.js'
|
||||
import { getIsLocked } from './getIsLocked.js'
|
||||
import { getMetaBySegment } from './getMetaBySegment.js'
|
||||
import { getVersions } from './getVersions.js'
|
||||
import { getViewsFromConfig } from './getViewsFromConfig.js'
|
||||
import { renderDocumentSlots } from './renderDocumentSlots.js'
|
||||
|
||||
export const generateMetadata: GenerateEditViewMetadata = async (args) => getMetaBySegment(args)
|
||||
|
||||
@@ -328,10 +328,14 @@ export type CollectionAdminOptions = {
|
||||
listMenuItems?: CustomComponent[]
|
||||
views?: {
|
||||
/**
|
||||
* Set to a React component to replace the entire Edit View, including all nested routes.
|
||||
* Set to an object to replace or modify individual nested routes, or to add new ones.
|
||||
* Replace, modify, or add new "document" views.
|
||||
* @link https://payloadcms.com/docs/custom-components/document-views
|
||||
*/
|
||||
edit?: EditConfig
|
||||
/**
|
||||
* Replace or modify the "list" view.
|
||||
* @link https://payloadcms.com/docs/custom-components/list-view
|
||||
*/
|
||||
list?: {
|
||||
actions?: CustomComponent[]
|
||||
Component?: PayloadComponent
|
||||
|
||||
@@ -340,29 +340,50 @@ export type Endpoint = {
|
||||
root?: never
|
||||
}
|
||||
|
||||
export type EditViewComponent = PayloadComponent<DocumentViewServerProps>
|
||||
/**
|
||||
* @deprecated
|
||||
* This type will be renamed in v4.
|
||||
* Use `DocumentViewComponent` instead.
|
||||
*/
|
||||
export type EditViewComponent = DocumentViewComponent
|
||||
|
||||
export type EditViewConfig = {
|
||||
export type DocumentViewComponent = PayloadComponent<DocumentViewServerProps>
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* This type will be renamed in v4.
|
||||
* Use `DocumentViewConfig` instead.
|
||||
*/
|
||||
export type EditViewConfig = DocumentViewConfig
|
||||
|
||||
type BaseDocumentViewConfig = {
|
||||
actions?: CustomComponent[]
|
||||
meta?: MetaConfig
|
||||
} & (
|
||||
| {
|
||||
actions?: CustomComponent[]
|
||||
}
|
||||
| {
|
||||
Component: EditViewComponent
|
||||
path?: string
|
||||
}
|
||||
| {
|
||||
path?: string
|
||||
/**
|
||||
* Add a new Edit View to the admin panel
|
||||
* i.e. you can render a custom view that has no tab, if desired
|
||||
* Or override a specific properties of an existing one
|
||||
* i.e. you can customize the `Default` view tab label, if desired
|
||||
*/
|
||||
tab?: DocumentTabConfig
|
||||
}
|
||||
)
|
||||
tab?: DocumentTabConfig
|
||||
}
|
||||
|
||||
/*
|
||||
If your view does not originate from a "known" key, e.g. `myCustomView`, then it is considered a "custom" view and can accept a `path`, etc.
|
||||
To render just a tab component without an accompanying view, you can omit the `path` and `Component` properties altogether.
|
||||
*/
|
||||
export type CustomDocumentViewConfig =
|
||||
| ({
|
||||
Component: DocumentViewComponent
|
||||
path: string
|
||||
} & BaseDocumentViewConfig)
|
||||
| ({
|
||||
Component?: DocumentViewComponent
|
||||
path?: never
|
||||
} & BaseDocumentViewConfig)
|
||||
|
||||
/*
|
||||
If your view does originates from a "known" key, e.g. `api`, then it is considered a "default" view and cannot accept a `path`, etc.
|
||||
*/
|
||||
export type DefaultDocumentViewConfig = {
|
||||
Component?: DocumentViewComponent
|
||||
} & BaseDocumentViewConfig
|
||||
|
||||
export type DocumentViewConfig = CustomDocumentViewConfig | DefaultDocumentViewConfig
|
||||
|
||||
export type Params = { [key: string]: string | string[] | undefined }
|
||||
|
||||
@@ -1260,46 +1281,46 @@ export type SanitizedConfig = {
|
||||
|
||||
export type EditConfig = EditConfigWithoutRoot | EditConfigWithRoot
|
||||
|
||||
/**
|
||||
* Replace or modify _all_ nested document views and routes, including the document header, controls, and tabs. This cannot be used in conjunction with other nested views.
|
||||
* + `root` - `/admin/collections/:collection/:id/**\/*`
|
||||
* @link https://payloadcms.com/docs/custom-components/document-views#document-root
|
||||
*/
|
||||
export type EditConfigWithRoot = {
|
||||
api?: never
|
||||
default?: never
|
||||
livePreview?: never
|
||||
/**
|
||||
* Replace or modify _all_ nested document views and routes, including the document header, controls, and tabs. This cannot be used in conjunction with other nested views.
|
||||
* + `root` - `/admin/collections/:collection/:id/**\/*`
|
||||
*/
|
||||
root: Partial<EditViewConfig>
|
||||
root: DefaultDocumentViewConfig
|
||||
version?: never
|
||||
versions?: never
|
||||
}
|
||||
|
||||
type KnownEditKeys = 'api' | 'default' | 'livePreview' | 'root' | 'version' | 'versions'
|
||||
|
||||
/**
|
||||
* Replace or modify individual nested routes, or add new ones:
|
||||
* + `default` - `/admin/collections/:collection/:id`
|
||||
* + `api` - `/admin/collections/:collection/:id/api`
|
||||
* + `livePreview` - `/admin/collections/:collection/:id/preview`
|
||||
* + `references` - `/admin/collections/:collection/:id/references`
|
||||
* + `relationships` - `/admin/collections/:collection/:id/relationships`
|
||||
* + `versions` - `/admin/collections/:collection/:id/versions`
|
||||
* + `version` - `/admin/collections/:collection/:id/versions/:version`
|
||||
* + `customView` - `/admin/collections/:collection/:id/:path`
|
||||
*
|
||||
* To override the entire Edit View including all nested views, use the `root` key.
|
||||
*
|
||||
* @link https://payloadcms.com/docs/custom-components/document-views
|
||||
*/
|
||||
export type EditConfigWithoutRoot = {
|
||||
[key: string]: EditViewConfig
|
||||
/**
|
||||
* Replace or modify individual nested routes, or add new ones:
|
||||
* + `default` - `/admin/collections/:collection/:id`
|
||||
* + `api` - `/admin/collections/:collection/:id/api`
|
||||
* + `livePreview` - `/admin/collections/:collection/:id/preview`
|
||||
* + `references` - `/admin/collections/:collection/:id/references`
|
||||
* + `relationships` - `/admin/collections/:collection/:id/relationships`
|
||||
* + `versions` - `/admin/collections/:collection/:id/versions`
|
||||
* + `version` - `/admin/collections/:collection/:id/versions/:version`
|
||||
* + `customView` - `/admin/collections/:collection/:id/:path`
|
||||
*
|
||||
* To override the entire Edit View including all nested views, use the `root` key.
|
||||
*/
|
||||
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
|
||||
api?: Partial<EditViewConfig>
|
||||
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
|
||||
default?: Partial<EditViewConfig>
|
||||
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
|
||||
livePreview?: Partial<EditViewConfig>
|
||||
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
|
||||
[K in Exclude<string, KnownEditKeys>]: CustomDocumentViewConfig
|
||||
} & {
|
||||
api?: DefaultDocumentViewConfig
|
||||
default?: DefaultDocumentViewConfig
|
||||
livePreview?: DefaultDocumentViewConfig
|
||||
root?: never
|
||||
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
|
||||
version?: Partial<EditViewConfig>
|
||||
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
|
||||
versions?: Partial<EditViewConfig>
|
||||
version?: DefaultDocumentViewConfig
|
||||
versions?: DefaultDocumentViewConfig
|
||||
}
|
||||
|
||||
export type EntityDescriptionComponent = CustomComponent
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type {
|
||||
BulkOperationResult,
|
||||
CustomDocumentViewConfig,
|
||||
DefaultDocumentViewConfig,
|
||||
JoinQuery,
|
||||
PaginatedDocs,
|
||||
SelectType,
|
||||
@@ -158,4 +160,17 @@ describe('Types testing', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('views', () => {
|
||||
test('default view config', () => {
|
||||
expect<DefaultDocumentViewConfig>().type.not.toBeAssignableWith<{
|
||||
path: string
|
||||
}>()
|
||||
|
||||
expect<CustomDocumentViewConfig>().type.toBeAssignableWith<{
|
||||
Component: string
|
||||
path: string
|
||||
}>()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user