fix(next): supports root document view overrides as separate from default edit view (#7673)
## Description We've since lost the ability to override the document view at the root-level. This was a feature that made it possible to override _the entire document routing/view structure_, including the document header/tabs and all nested routes within, i.e. the API route/view, the Live Preview route/view, etc. This is distinct from the "default" edit view, which _only_ targets the component rendered within the "edit" tab. This regression was introduced when types were simplified down to better support "component paths" here: #7620. The `default` key was incorrectly used as the "root" view override. To continue to support stricter types _and_ root view overrides, a new `root` key has been added to the `views` config. You were previously able to do this: ```tsx import { MyComponent } from './MyComponent.js' export const MyCollection = { // ... admin: { views: { Edit: MyComponent } } } ``` This is now done like this: ```tsx export const MyCollection = { // ... admin: { views: { edit: { root: { Component: './path-to-my-component.js' } } } } } ``` Some of the documentation was also incorrect according to the new component paths API. - [x] I have read and understand the [CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md) document in this repository. ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) - [x] This change requires a documentation update ## Checklist: - [x] Existing test suite passes locally with my changes - [x] I have made corresponding changes to the documentation
This commit is contained in:
@@ -53,7 +53,7 @@ For more granular control, pass a configuration object instead. Payload exposes
|
||||
|
||||
| Property | Description |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **`Component`** \* | Pass in the component that should be rendered when a user navigates to this route. |
|
||||
| **`Component`** \* | Pass in the component path that should be rendered when a user navigates to this route. |
|
||||
| **`path`** \* | Any valid URL path or array of paths that [`path-to-regexp`](https://www.npmjs.com/package/path-to-regex) understands. |
|
||||
| **`exact`** | Boolean. When true, will only match if the path matches the `usePathname()` exactly. |
|
||||
| **`strict`** | When true, a path that has a trailing slash will only match a `location.pathname` with a trailing slash. This has no effect when there are additional URL segments in the pathname. |
|
||||
@@ -111,7 +111,20 @@ export const MyCollectionConfig: SanitizedCollectionConfig = {
|
||||
components: {
|
||||
views: {
|
||||
edit: {
|
||||
Component: '/path/to/MyCustomEditView', // highlight-line
|
||||
root: {
|
||||
Component: '/path/to/MyCustomEditView', // highlight-line
|
||||
}
|
||||
// other options include:
|
||||
// default
|
||||
// versions
|
||||
// version
|
||||
// api
|
||||
// livePreview
|
||||
// [key: string]
|
||||
// See "Document Views" for more details
|
||||
},
|
||||
list: {
|
||||
Component: '/path/to/MyCustomListView',
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -123,7 +136,7 @@ _For details on how to build Custom Views, see [Building Custom Views](#building
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
The `Edit` property will replace the _entire_ Edit View, including the title, tabs, etc., _as well as all nested [Document Views](#document-views)_, such as the API, Live Preview, and Version views. To replace only the Edit View precisely, use the `Edit.Default` key instead.
|
||||
The `root` property will replace the _entire_ Edit View, including the title, tabs, etc., _as well as all nested [Document Views](#document-views)_, such as the API, Live Preview, and Version views. To replace only the Edit View precisely, use the `edit.default` key instead.
|
||||
</Banner>
|
||||
|
||||
The following options are available:
|
||||
@@ -152,18 +165,29 @@ export const MyGlobalConfig: SanitizedGlobalConfig = {
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
edit: '/path/to/MyCustomEditView', // highlight-line
|
||||
edit: {
|
||||
root: {
|
||||
Component: '/path/to/MyCustomEditView', // highlight-line
|
||||
}
|
||||
// other options include:
|
||||
// default
|
||||
// versions
|
||||
// version
|
||||
// api
|
||||
// livePreview
|
||||
// [key: string]
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
_For details on how to build Custom Views, see [Building Custom Views](#building-custom-views)._
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
The `Edit` property will replace the _entire_ Edit View, including the title, tabs, etc., _as well as all nested [Document Views](#document-views)_, such as the API, Live Preview, and Version views. To replace only the Edit View precisely, use the `Edit.Default` key instead.
|
||||
The `root` property will replace the _entire_ Edit View, including the title, tabs, etc., _as well as all nested [Document Views](#document-views)_, such as the API, Live Preview, and Version views. To replace only the Edit View precisely, use the `edit.default` key instead.
|
||||
</Banner>
|
||||
|
||||
The following options are available:
|
||||
@@ -199,25 +223,26 @@ export const MyCollectionOrGlobalConfig: SanitizedCollectionConfig = {
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
_For details on how to build Custom Views, see [Building Custom Views](#building-custom-views)._
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
If you need to replace the _entire_ Edit View, including _all_ nested Document Views, use the `Edit` key itself. See [Custom Collection Views](#collection-views) or [Custom Global Views](#global-views) for more information.
|
||||
If you need to replace the _entire_ Edit View, including _all_ nested Document Views, use the `root` key. See [Custom Collection Views](#collection-views) or [Custom Global Views](#global-views) for more information.
|
||||
</Banner>
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Property | Description |
|
||||
| ----------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`default`** | The Default view is the primary view in which your document is edited. |
|
||||
| **`versions`** | The Versions view is used to view the version history of a single document. [More details](../versions). |
|
||||
| **`version`** | The Version view is used to view a single version of a single document for a given collection. [More details](../versions). |
|
||||
| **`api`** | The API view is used to display the REST API JSON response for a given document. |
|
||||
| **`livePreview`** | The LivePreview view is used to display the Live Preview interface. [More details](../live-preview). |
|
||||
| **`root`** | The Root View overrides all other nested views and routes. No document controls or tabs are rendered when this key is set. |
|
||||
| **`default`** | The Default View is the primary view in which your document is edited. It is rendered within the "Edit" tab. |
|
||||
| **`versions`** | The Versions View is used to navigate the version history of a single document. It is rendered within the "Versions" tab. [More details](../versions). |
|
||||
| **`version`** | The Version View is used to edit a single version of a document. It is rendered within the "Version" tab. [More details](../versions). |
|
||||
| **`api`** | The API View is used to display the REST API JSON response for a given document. It is rendered within the "API" tab. |
|
||||
| **`livePreview`** | The LivePreview view is used to display the Live Preview interface. It is rendered within the "Live Preview" tab. [More details](../live-preview). |
|
||||
|
||||
### Document Tabs
|
||||
|
||||
|
||||
@@ -66,139 +66,89 @@ export const getViewsFromConfig = ({
|
||||
config?.admin?.livePreview?.globals?.includes(globalConfig?.slug)
|
||||
|
||||
if (collectionConfig) {
|
||||
const editConfig = collectionConfig?.admin?.components?.views?.edit
|
||||
const EditOverride = typeof editConfig === 'function' ? editConfig : null
|
||||
const [collectionEntity, collectionSlug, segment3, segment4, segment5, ...remainingSegments] =
|
||||
routeSegments
|
||||
|
||||
if (EditOverride) {
|
||||
CustomView = EditOverride
|
||||
}
|
||||
|
||||
if (!EditOverride) {
|
||||
const [collectionEntity, collectionSlug, segment3, segment4, segment5, ...remainingSegments] =
|
||||
routeSegments
|
||||
|
||||
if (!docPermissions?.read?.permission) {
|
||||
notFound()
|
||||
} else {
|
||||
// `../:id`, or `../create`
|
||||
switch (routeSegments.length) {
|
||||
case 3: {
|
||||
switch (segment3) {
|
||||
case 'create': {
|
||||
if ('create' in docPermissions && docPermissions?.create?.permission) {
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByKey(views, 'default'),
|
||||
}
|
||||
DefaultView = {
|
||||
Component: DefaultEditView,
|
||||
}
|
||||
} else {
|
||||
ErrorView = {
|
||||
Component: UnauthorizedView,
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
if (!docPermissions?.read?.permission) {
|
||||
notFound()
|
||||
} else {
|
||||
// `../:id`, or `../create`
|
||||
switch (routeSegments.length) {
|
||||
case 3: {
|
||||
switch (segment3) {
|
||||
case 'create': {
|
||||
if ('create' in docPermissions && docPermissions?.create?.permission) {
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByKey(views, 'default'),
|
||||
}
|
||||
DefaultView = {
|
||||
Component: DefaultEditView,
|
||||
}
|
||||
break
|
||||
} else {
|
||||
ErrorView = {
|
||||
Component: UnauthorizedView,
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// `../:id/api`, `../:id/preview`, `../:id/versions`, etc
|
||||
case 4: {
|
||||
switch (segment4) {
|
||||
case 'api': {
|
||||
if (collectionConfig?.admin?.hideAPIURL !== true) {
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByKey(views, 'api'),
|
||||
}
|
||||
DefaultView = {
|
||||
Component: DefaultAPIView,
|
||||
}
|
||||
}
|
||||
break
|
||||
default: {
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByKey(views, 'default'),
|
||||
}
|
||||
|
||||
case 'preview': {
|
||||
if (livePreviewEnabled) {
|
||||
DefaultView = {
|
||||
Component: DefaultLivePreviewView,
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'versions': {
|
||||
if (docPermissions?.readVersions?.permission) {
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByKey(views, 'versions'),
|
||||
}
|
||||
DefaultView = {
|
||||
Component: DefaultVersionsView,
|
||||
}
|
||||
} else {
|
||||
ErrorView = {
|
||||
Component: UnauthorizedView,
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
const baseRoute = [
|
||||
adminRoute !== '/' && adminRoute,
|
||||
'collections',
|
||||
collectionSlug,
|
||||
segment3,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
|
||||
const currentRoute = [baseRoute, segment4, segment5, ...remainingSegments]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByRoute({
|
||||
baseRoute,
|
||||
currentRoute,
|
||||
views,
|
||||
}),
|
||||
}
|
||||
break
|
||||
DefaultView = {
|
||||
Component: DefaultEditView,
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// `../:id/versions/:version`, etc
|
||||
default: {
|
||||
if (segment4 === 'versions') {
|
||||
if (docPermissions?.readVersions?.permission) {
|
||||
// `../:id/api`, `../:id/preview`, `../:id/versions`, etc
|
||||
case 4: {
|
||||
switch (segment4) {
|
||||
case 'api': {
|
||||
if (collectionConfig?.admin?.hideAPIURL !== true) {
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByKey(views, 'version'),
|
||||
payloadComponent: getCustomViewByKey(views, 'api'),
|
||||
}
|
||||
DefaultView = {
|
||||
Component: DefaultVersionView,
|
||||
Component: DefaultAPIView,
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'preview': {
|
||||
if (livePreviewEnabled) {
|
||||
DefaultView = {
|
||||
Component: DefaultLivePreviewView,
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'versions': {
|
||||
if (docPermissions?.readVersions?.permission) {
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByKey(views, 'versions'),
|
||||
}
|
||||
DefaultView = {
|
||||
Component: DefaultVersionsView,
|
||||
}
|
||||
} else {
|
||||
ErrorView = {
|
||||
Component: UnauthorizedView,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
const baseRoute = [
|
||||
adminRoute !== '/' && adminRoute,
|
||||
collectionEntity,
|
||||
'collections',
|
||||
collectionSlug,
|
||||
segment3,
|
||||
]
|
||||
@@ -216,144 +166,176 @@ export const getViewsFromConfig = ({
|
||||
views,
|
||||
}),
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// `../:id/versions/:version`, etc
|
||||
default: {
|
||||
if (segment4 === 'versions') {
|
||||
if (docPermissions?.readVersions?.permission) {
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByKey(views, 'version'),
|
||||
}
|
||||
DefaultView = {
|
||||
Component: DefaultVersionView,
|
||||
}
|
||||
} else {
|
||||
ErrorView = {
|
||||
Component: UnauthorizedView,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const baseRoute = [
|
||||
adminRoute !== '/' && adminRoute,
|
||||
collectionEntity,
|
||||
collectionSlug,
|
||||
segment3,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
|
||||
const currentRoute = [baseRoute, segment4, segment5, ...remainingSegments]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByRoute({
|
||||
baseRoute,
|
||||
currentRoute,
|
||||
views,
|
||||
}),
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (globalConfig) {
|
||||
const editConfig = globalConfig?.admin?.components?.views?.edit
|
||||
const EditOverride = typeof editConfig === 'function' ? editConfig : null
|
||||
const [globalEntity, globalSlug, segment3, ...remainingSegments] = routeSegments
|
||||
|
||||
if (EditOverride) {
|
||||
CustomView = EditOverride
|
||||
}
|
||||
|
||||
if (!EditOverride) {
|
||||
const [globalEntity, globalSlug, segment3, ...remainingSegments] = routeSegments
|
||||
|
||||
if (!docPermissions?.read?.permission) {
|
||||
notFound()
|
||||
} else {
|
||||
switch (routeSegments.length) {
|
||||
case 2: {
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByKey(views, 'default'),
|
||||
}
|
||||
DefaultView = {
|
||||
Component: DefaultEditView,
|
||||
}
|
||||
break
|
||||
if (!docPermissions?.read?.permission) {
|
||||
notFound()
|
||||
} else {
|
||||
switch (routeSegments.length) {
|
||||
case 2: {
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByKey(views, 'default'),
|
||||
}
|
||||
|
||||
case 3: {
|
||||
// `../:slug/api`, `../:slug/preview`, `../:slug/versions`, etc
|
||||
switch (segment3) {
|
||||
case 'api': {
|
||||
if (globalConfig?.admin?.hideAPIURL !== true) {
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByKey(views, 'api'),
|
||||
}
|
||||
DefaultView = {
|
||||
Component: DefaultAPIView,
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'preview': {
|
||||
if (livePreviewEnabled) {
|
||||
DefaultView = {
|
||||
Component: DefaultLivePreviewView,
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'versions': {
|
||||
if (docPermissions?.readVersions?.permission) {
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByKey(views, 'versions'),
|
||||
}
|
||||
DefaultView = {
|
||||
Component: DefaultVersionsView,
|
||||
}
|
||||
} else {
|
||||
ErrorView = {
|
||||
Component: UnauthorizedView,
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
if (docPermissions?.read?.permission) {
|
||||
const baseRoute = [adminRoute, globalEntity, globalSlug, segment3]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
|
||||
const currentRoute = [baseRoute, segment3, ...remainingSegments]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByRoute({
|
||||
baseRoute,
|
||||
currentRoute,
|
||||
views,
|
||||
}),
|
||||
}
|
||||
DefaultView = {
|
||||
Component: DefaultEditView,
|
||||
}
|
||||
} else {
|
||||
ErrorView = {
|
||||
Component: UnauthorizedView,
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
DefaultView = {
|
||||
Component: DefaultEditView,
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
// `../:slug/versions/:version`, etc
|
||||
if (segment3 === 'versions') {
|
||||
if (docPermissions?.readVersions?.permission) {
|
||||
case 3: {
|
||||
// `../:slug/api`, `../:slug/preview`, `../:slug/versions`, etc
|
||||
switch (segment3) {
|
||||
case 'api': {
|
||||
if (globalConfig?.admin?.hideAPIURL !== true) {
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByKey(views, 'version'),
|
||||
payloadComponent: getCustomViewByKey(views, 'api'),
|
||||
}
|
||||
DefaultView = {
|
||||
Component: DefaultVersionView,
|
||||
Component: DefaultAPIView,
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'preview': {
|
||||
if (livePreviewEnabled) {
|
||||
DefaultView = {
|
||||
Component: DefaultLivePreviewView,
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'versions': {
|
||||
if (docPermissions?.readVersions?.permission) {
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByKey(views, 'versions'),
|
||||
}
|
||||
DefaultView = {
|
||||
Component: DefaultVersionsView,
|
||||
}
|
||||
} else {
|
||||
ErrorView = {
|
||||
Component: UnauthorizedView,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const baseRoute = [adminRoute !== '/' && adminRoute, 'globals', globalSlug]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
break
|
||||
}
|
||||
|
||||
const currentRoute = [baseRoute, segment3, ...remainingSegments]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
default: {
|
||||
if (docPermissions?.read?.permission) {
|
||||
const baseRoute = [adminRoute, globalEntity, globalSlug, segment3]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
|
||||
const currentRoute = [baseRoute, segment3, ...remainingSegments]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByRoute({
|
||||
baseRoute,
|
||||
currentRoute,
|
||||
views,
|
||||
}),
|
||||
}
|
||||
DefaultView = {
|
||||
Component: DefaultEditView,
|
||||
}
|
||||
} else {
|
||||
ErrorView = {
|
||||
Component: UnauthorizedView,
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
// `../:slug/versions/:version`, etc
|
||||
if (segment3 === 'versions') {
|
||||
if (docPermissions?.readVersions?.permission) {
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByRoute({
|
||||
baseRoute,
|
||||
currentRoute,
|
||||
views,
|
||||
}),
|
||||
payloadComponent: getCustomViewByKey(views, 'version'),
|
||||
}
|
||||
DefaultView = {
|
||||
Component: DefaultVersionView,
|
||||
}
|
||||
} else {
|
||||
ErrorView = {
|
||||
Component: UnauthorizedView,
|
||||
}
|
||||
}
|
||||
break
|
||||
} else {
|
||||
const baseRoute = [adminRoute !== '/' && adminRoute, 'globals', globalSlug]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
|
||||
const currentRoute = [baseRoute, segment3, ...remainingSegments]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
|
||||
CustomView = {
|
||||
payloadComponent: getCustomViewByRoute({
|
||||
baseRoute,
|
||||
currentRoute,
|
||||
views,
|
||||
}),
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
|
||||
const isEditing = getIsEditing({ id, collectionSlug, globalSlug })
|
||||
|
||||
let ViewOverride: MappedComponent<ServerSideEditViewProps>
|
||||
let RootViewOverride: MappedComponent<ServerSideEditViewProps>
|
||||
let CustomView: MappedComponent<ServerSideEditViewProps>
|
||||
let DefaultView: MappedComponent<ServerSideEditViewProps>
|
||||
let ErrorView: MappedComponent<AdminViewProps>
|
||||
@@ -115,19 +115,18 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
|
||||
apiURL = `${serverURL}${apiRoute}/${collectionSlug}/${id}${apiQueryParams}`
|
||||
|
||||
ViewOverride =
|
||||
collectionConfig?.admin?.components?.views?.edit?.default &&
|
||||
'Component' in collectionConfig.admin.components.views.edit.default
|
||||
RootViewOverride =
|
||||
collectionConfig?.admin?.components?.views?.edit?.root &&
|
||||
'Component' in collectionConfig.admin.components.views.edit.root
|
||||
? createMappedComponent(
|
||||
collectionConfig?.admin?.components?.views?.edit?.default
|
||||
?.Component as EditViewComponent, // some type info gets lost from Config => SanitizedConfig due to our usage of Deep type operations from ts-essentials. Despite .Component being defined as EditViewComponent, this info is lost and we need cast it here.
|
||||
collectionConfig?.admin?.components?.views?.edit?.root?.Component as EditViewComponent, // some type info gets lost from Config => SanitizedConfig due to our usage of Deep type operations from ts-essentials. Despite .Component being defined as EditViewComponent, this info is lost and we need cast it here.
|
||||
undefined,
|
||||
undefined,
|
||||
'collectionConfig?.admin?.components?.views?.edit?.default',
|
||||
'collectionConfig?.admin?.components?.views?.edit?.root',
|
||||
)
|
||||
: null
|
||||
|
||||
if (!ViewOverride) {
|
||||
if (!RootViewOverride) {
|
||||
const collectionViews = getViewsFromConfig({
|
||||
collectionConfig,
|
||||
config,
|
||||
@@ -157,7 +156,7 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
)
|
||||
}
|
||||
|
||||
if (!CustomView && !DefaultView && !ViewOverride && !ErrorView) {
|
||||
if (!CustomView && !DefaultView && !RootViewOverride && !ErrorView) {
|
||||
ErrorView = createMappedComponent(undefined, undefined, NotFoundView, 'NotFoundView')
|
||||
}
|
||||
}
|
||||
@@ -170,9 +169,11 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
const params = new URLSearchParams({
|
||||
locale: locale?.code,
|
||||
})
|
||||
|
||||
if (globalConfig.versions?.drafts) {
|
||||
params.append('draft', 'true')
|
||||
}
|
||||
|
||||
if (locale?.code) {
|
||||
params.append('locale', locale.code)
|
||||
}
|
||||
@@ -181,10 +182,18 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
|
||||
apiURL = `${serverURL}${apiRoute}/${globalSlug}${apiQueryParams}`
|
||||
|
||||
const editConfig = globalConfig?.admin?.components?.views?.edit
|
||||
ViewOverride = typeof editConfig === 'function' ? editConfig : null
|
||||
RootViewOverride =
|
||||
globalConfig?.admin?.components?.views?.edit?.root &&
|
||||
'Component' in globalConfig.admin.components.views.edit.root
|
||||
? createMappedComponent(
|
||||
globalConfig?.admin?.components?.views?.edit?.root?.Component as EditViewComponent, // some type info gets lost from Config => SanitizedConfig due to our usage of Deep type operations from ts-essentials. Despite .Component being defined as EditViewComponent, this info is lost and we need cast it here.
|
||||
undefined,
|
||||
undefined,
|
||||
'globalConfig?.admin?.components?.views?.edit?.root',
|
||||
)
|
||||
: null
|
||||
|
||||
if (!ViewOverride) {
|
||||
if (!RootViewOverride) {
|
||||
const globalViews = getViewsFromConfig({
|
||||
config,
|
||||
docPermissions,
|
||||
@@ -213,7 +222,7 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
'globalViews?.ErrorView.payloadComponent',
|
||||
)
|
||||
|
||||
if (!CustomView && !DefaultView && !ViewOverride && !ErrorView) {
|
||||
if (!CustomView && !DefaultView && !RootViewOverride && !ErrorView) {
|
||||
ErrorView = createMappedComponent(undefined, undefined, NotFoundView, 'NotFoundView')
|
||||
}
|
||||
}
|
||||
@@ -268,7 +277,7 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
initialState={formState}
|
||||
isEditing={isEditing}
|
||||
>
|
||||
{!ViewOverride && (
|
||||
{!RootViewOverride && (
|
||||
<DocumentHeader
|
||||
collectionConfig={collectionConfig}
|
||||
globalConfig={globalConfig}
|
||||
@@ -294,7 +303,9 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
<RenderComponent mappedComponent={ErrorView} />
|
||||
) : (
|
||||
<RenderComponent
|
||||
mappedComponent={ViewOverride ? ViewOverride : CustomView ? CustomView : DefaultView}
|
||||
mappedComponent={
|
||||
RootViewOverride ? RootViewOverride : CustomView ? CustomView : DefaultView
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</EditDepthProvider>
|
||||
|
||||
@@ -912,28 +912,44 @@ export type SanitizedConfig = {
|
||||
'collections' | 'editor' | 'endpoint' | 'globals' | 'i18n' | 'localization' | 'upload'
|
||||
>
|
||||
|
||||
export type EditConfig = {
|
||||
[key: string]: Partial<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`
|
||||
*/
|
||||
api?: Partial<EditViewConfig>
|
||||
default?: Partial<EditViewConfig>
|
||||
livePreview?: Partial<EditViewConfig>
|
||||
version?: Partial<EditViewConfig>
|
||||
versions?: Partial<EditViewConfig>
|
||||
// TODO: uncomment these as they are built
|
||||
// references?: EditView
|
||||
// relationships?: EditView
|
||||
}
|
||||
export type EditConfig =
|
||||
| {
|
||||
[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.
|
||||
*/
|
||||
api?: Partial<EditViewConfig>
|
||||
default?: Partial<EditViewConfig>
|
||||
livePreview?: Partial<EditViewConfig>
|
||||
root?: never
|
||||
version?: Partial<EditViewConfig>
|
||||
versions?: Partial<EditViewConfig>
|
||||
// TODO: uncomment these as they are built
|
||||
// references?: EditView
|
||||
// relationships?: EditView
|
||||
}
|
||||
| {
|
||||
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>
|
||||
version?: never
|
||||
versions?: never
|
||||
}
|
||||
|
||||
export type EntityDescriptionComponent = CustomComponent
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export const CustomViews1: CollectionConfig = {
|
||||
// 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: {
|
||||
default: {
|
||||
root: {
|
||||
Component: '/components/views/CustomEdit/index.js#CustomEditView',
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user