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:
Jessica Chowdhury
2024-09-16 10:05:15 -04:00
committed by GitHub
parent 7c8272b467
commit 3b59416298
8 changed files with 68 additions and 8 deletions

View File

@@ -276,7 +276,8 @@ The following options are available:
| **`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. |
| **`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). |
<Banner type="success">

View File

@@ -10,17 +10,22 @@
}
&__nav-toggler-wrapper {
position: fixed;
position: sticky;
z-index: var(--z-modal);
top: 0;
left: 0;
height: var(--app-header-height);
height: 0;
width: var(--gutter-h);
display: flex;
align-items: center;
justify-content: center;
}
&__nav-toggler {
height: var(--app-header-height);
display: flex;
align-items: center;
}
&__wrap {
min-width: 0;
width: 100%;

View File

@@ -31,7 +31,8 @@ export const DefaultTemplate: React.FC<DefaultTemplateProps> = ({
}) => {
const {
admin: {
components: { Nav: CustomNav } = {
components: { header: CustomHeader, Nav: CustomNav } = {
header: undefined,
Nav: undefined,
},
} = {},
@@ -57,10 +58,18 @@ export const DefaultTemplate: React.FC<DefaultTemplateProps> = ({
'CustomNav',
)
const MappedCustomHeader = createMappedComponent(
CustomHeader,
undefined,
undefined,
'CustomHeader',
)
return (
<EntityVisibilityProvider visibleEntities={visibleEntities}>
<BulkUploadProvider>
<div>
<RenderComponent mappedComponent={MappedCustomHeader} />
<div style={{ position: 'relative' }}>
<div className={`${baseClass}__nav-toggler-wrapper`} id="nav-toggler">
<NavToggler className={`${baseClass}__nav-toggler`}>
<NavHamburger />

View File

@@ -36,10 +36,12 @@ export function iterateConfig({
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?.header)
addToImportMap(config.admin?.components?.logout?.Button)
addToImportMap(config.admin?.components?.graphics?.Icon)
addToImportMap(config.admin?.components?.graphics?.Logo)

View File

@@ -697,6 +697,10 @@ export type Config = {
/** Replace the logo on the login page */
Logo?: CustomComponent
}
/**
* Add custom header to top of page globally
*/
header?: CustomComponent[]
/** Replace logout related components */
logout?: {
/** Replace the logout button */

View 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>
)
}

View File

@@ -47,6 +47,7 @@ export default buildConfigWithDefaults({
],
afterNavLinks: ['/components/AfterNavLinks/index.js#AfterNavLinks'],
beforeLogin: ['/components/BeforeLogin/index.js#BeforeLogin'],
header: ['/components/CustomHeader/index.js#CustomHeader'],
logout: {
Button: '/components/Logout/index.js#Logout',
},

View File

@@ -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', () => {
test('collection — should not show API tab when disabled in config', async () => {
await page.goto(postsUrl.collection(noApiViewCollectionSlug))