feat: add new option for admin.components.header (#7647)
## Description Adds `admin.components.header` option to allow users to insert custom components in the page header / top of page. [Related discussion](https://github.com/payloadcms/payload/discussions/7584) - [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 - [ ] Chore (non-breaking change which does not add functionality) - [X] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Change to the [templates](https://github.com/payloadcms/payload/tree/main/templates) directory (does not affect core functionality) - [ ] Change to the [examples](https://github.com/payloadcms/payload/tree/main/examples) directory (does not affect core functionality) - [x] This change requires a documentation update ## Checklist: - [x] I have added tests that prove my fix is effective or that my feature works - will add - [X] Existing test suite passes locally with my changes
This commit is contained in:
committed by
GitHub
parent
7c8272b467
commit
3b59416298
@@ -276,7 +276,8 @@ The following options are available:
|
|||||||
| **`graphics.Icon`** | The simplified logo used in contexts like the the `Nav` component. |
|
| **`graphics.Icon`** | The simplified logo used in contexts like the the `Nav` component. |
|
||||||
| **`graphics.Logo`** | The full logo used in contexts like the `Login` view. |
|
| **`graphics.Logo`** | The full logo used in contexts like the `Login` view. |
|
||||||
| **`providers`** | Custom [React Context](https://react.dev/learn/scaling-up-with-reducer-and-context) providers that will wrap the entire Admin Panel. [More details](#custom-providers). |
|
| **`providers`** | Custom [React Context](https://react.dev/learn/scaling-up-with-reducer-and-context) providers that will wrap the entire Admin Panel. [More details](#custom-providers). |
|
||||||
| **`actions`** | An array of Custom Components to be rendered in the header of the Admin Panel, providing additional interactivity and functionality. |
|
| **`actions`** | An array of Custom Components to be rendered _within_ the header of the Admin Panel, providing additional interactivity and functionality. |
|
||||||
|
| **`header`** | An array of Custom Components to be injected above the Payload header. |
|
||||||
| **`views`** | Override or create new views within the Admin Panel. [More details](./views). |
|
| **`views`** | Override or create new views within the Admin Panel. [More details](./views). |
|
||||||
|
|
||||||
<Banner type="success">
|
<Banner type="success">
|
||||||
|
|||||||
@@ -10,17 +10,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__nav-toggler-wrapper {
|
&__nav-toggler-wrapper {
|
||||||
position: fixed;
|
position: sticky;
|
||||||
z-index: var(--z-modal);
|
z-index: var(--z-modal);
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
height: var(--app-header-height);
|
height: 0;
|
||||||
width: var(--gutter-h);
|
width: var(--gutter-h);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__nav-toggler {
|
||||||
|
height: var(--app-header-height);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
&__wrap {
|
&__wrap {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ export const DefaultTemplate: React.FC<DefaultTemplateProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
admin: {
|
admin: {
|
||||||
components: { Nav: CustomNav } = {
|
components: { header: CustomHeader, Nav: CustomNav } = {
|
||||||
|
header: undefined,
|
||||||
Nav: undefined,
|
Nav: undefined,
|
||||||
},
|
},
|
||||||
} = {},
|
} = {},
|
||||||
@@ -57,10 +58,18 @@ export const DefaultTemplate: React.FC<DefaultTemplateProps> = ({
|
|||||||
'CustomNav',
|
'CustomNav',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const MappedCustomHeader = createMappedComponent(
|
||||||
|
CustomHeader,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
'CustomHeader',
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EntityVisibilityProvider visibleEntities={visibleEntities}>
|
<EntityVisibilityProvider visibleEntities={visibleEntities}>
|
||||||
<BulkUploadProvider>
|
<BulkUploadProvider>
|
||||||
<div>
|
<RenderComponent mappedComponent={MappedCustomHeader} />
|
||||||
|
<div style={{ position: 'relative' }}>
|
||||||
<div className={`${baseClass}__nav-toggler-wrapper`} id="nav-toggler">
|
<div className={`${baseClass}__nav-toggler-wrapper`} id="nav-toggler">
|
||||||
<NavToggler className={`${baseClass}__nav-toggler`}>
|
<NavToggler className={`${baseClass}__nav-toggler`}>
|
||||||
<NavHamburger />
|
<NavHamburger />
|
||||||
|
|||||||
@@ -36,10 +36,12 @@ export function iterateConfig({
|
|||||||
imports,
|
imports,
|
||||||
})
|
})
|
||||||
|
|
||||||
typeof config.admin?.avatar === 'object' && addToImportMap(config.admin?.avatar?.Component)
|
if (typeof config.admin?.avatar === 'object') {
|
||||||
|
addToImportMap(config.admin?.avatar?.Component)
|
||||||
|
}
|
||||||
|
|
||||||
addToImportMap(config.admin?.components?.Nav)
|
addToImportMap(config.admin?.components?.Nav)
|
||||||
|
addToImportMap(config.admin?.components?.header)
|
||||||
addToImportMap(config.admin?.components?.logout?.Button)
|
addToImportMap(config.admin?.components?.logout?.Button)
|
||||||
addToImportMap(config.admin?.components?.graphics?.Icon)
|
addToImportMap(config.admin?.components?.graphics?.Icon)
|
||||||
addToImportMap(config.admin?.components?.graphics?.Logo)
|
addToImportMap(config.admin?.components?.graphics?.Logo)
|
||||||
|
|||||||
@@ -697,6 +697,10 @@ export type Config = {
|
|||||||
/** Replace the logo on the login page */
|
/** Replace the logo on the login page */
|
||||||
Logo?: CustomComponent
|
Logo?: CustomComponent
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Add custom header to top of page globally
|
||||||
|
*/
|
||||||
|
header?: CustomComponent[]
|
||||||
/** Replace logout related components */
|
/** Replace logout related components */
|
||||||
logout?: {
|
logout?: {
|
||||||
/** Replace the logout button */
|
/** Replace the logout button */
|
||||||
|
|||||||
30
test/admin/components/CustomHeader/index.tsx
Normal file
30
test/admin/components/CustomHeader/index.tsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import type { PayloadServerReactComponent, SanitizedConfig } from 'payload'
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const baseClass = 'custom-header'
|
||||||
|
|
||||||
|
export const CustomHeader: PayloadServerReactComponent<
|
||||||
|
SanitizedConfig['admin']['components']['header'][0]
|
||||||
|
> = () => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={baseClass}
|
||||||
|
style={{
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: 'var(--theme-success-500)',
|
||||||
|
display: 'flex',
|
||||||
|
minHeight: 'var(--app-header-height)',
|
||||||
|
padding: '0 var(--gutter-h)',
|
||||||
|
// position: 'sticky',
|
||||||
|
top: 0,
|
||||||
|
width: '100%',
|
||||||
|
zIndex: 'var(--z-modal)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p style={{ color: 'var(--theme-text)', margin: 0 }}>
|
||||||
|
Here is a custom header inserted with admin.components.header
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -47,6 +47,7 @@ export default buildConfigWithDefaults({
|
|||||||
],
|
],
|
||||||
afterNavLinks: ['/components/AfterNavLinks/index.js#AfterNavLinks'],
|
afterNavLinks: ['/components/AfterNavLinks/index.js#AfterNavLinks'],
|
||||||
beforeLogin: ['/components/BeforeLogin/index.js#BeforeLogin'],
|
beforeLogin: ['/components/BeforeLogin/index.js#BeforeLogin'],
|
||||||
|
header: ['/components/CustomHeader/index.js#CustomHeader'],
|
||||||
logout: {
|
logout: {
|
||||||
Button: '/components/Logout/index.js#Logout',
|
Button: '/components/Logout/index.js#Logout',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -680,6 +680,14 @@ describe('admin1', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('custom components', () => {
|
||||||
|
test('should render custom header', async () => {
|
||||||
|
await page.goto(`${serverURL}/admin`)
|
||||||
|
const header = page.locator('.custom-header')
|
||||||
|
await expect(header).toContainText('Here is a custom header')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('API view', () => {
|
describe('API view', () => {
|
||||||
test('collection — should not show API tab when disabled in config', async () => {
|
test('collection — should not show API tab when disabled in config', async () => {
|
||||||
await page.goto(postsUrl.collection(noApiViewCollectionSlug))
|
await page.goto(postsUrl.collection(noApiViewCollectionSlug))
|
||||||
|
|||||||
Reference in New Issue
Block a user