Skip to content

Commit 3b59416

Browse files
authored
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](#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
1 parent 7c8272b commit 3b59416

File tree

8 files changed

+68
-8
lines changed

8 files changed

+68
-8
lines changed

docs/admin/components.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ The following options are available:
276276
| **`graphics.Icon`** | The simplified logo used in contexts like the the `Nav` component. |
277277
| **`graphics.Logo`** | The full logo used in contexts like the `Login` view. |
278278
| **`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). |
279-
| **`actions`** | An array of Custom Components to be rendered in the header of the Admin Panel, providing additional interactivity and functionality. |
279+
| **`actions`** | An array of Custom Components to be rendered _within_ the header of the Admin Panel, providing additional interactivity and functionality. |
280+
| **`header`** | An array of Custom Components to be injected above the Payload header. |
280281
| **`views`** | Override or create new views within the Admin Panel. [More details](./views). |
281282

282283
<Banner type="success">

packages/next/src/templates/Default/index.scss

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,22 @@
1010
}
1111

1212
&__nav-toggler-wrapper {
13-
position: fixed;
13+
position: sticky;
1414
z-index: var(--z-modal);
1515
top: 0;
1616
left: 0;
17-
height: var(--app-header-height);
17+
height: 0;
1818
width: var(--gutter-h);
1919
display: flex;
20-
align-items: center;
2120
justify-content: center;
2221
}
2322

23+
&__nav-toggler {
24+
height: var(--app-header-height);
25+
display: flex;
26+
align-items: center;
27+
}
28+
2429
&__wrap {
2530
min-width: 0;
2631
width: 100%;

packages/next/src/templates/Default/index.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ export const DefaultTemplate: React.FC<DefaultTemplateProps> = ({
3131
}) => {
3232
const {
3333
admin: {
34-
components: { Nav: CustomNav } = {
34+
components: { header: CustomHeader, Nav: CustomNav } = {
35+
header: undefined,
3536
Nav: undefined,
3637
},
3738
} = {},
@@ -57,10 +58,18 @@ export const DefaultTemplate: React.FC<DefaultTemplateProps> = ({
5758
'CustomNav',
5859
)
5960

61+
const MappedCustomHeader = createMappedComponent(
62+
CustomHeader,
63+
undefined,
64+
undefined,
65+
'CustomHeader',
66+
)
67+
6068
return (
6169
<EntityVisibilityProvider visibleEntities={visibleEntities}>
6270
<BulkUploadProvider>
63-
<div>
71+
<RenderComponent mappedComponent={MappedCustomHeader} />
72+
<div style={{ position: 'relative' }}>
6473
<div className={`${baseClass}__nav-toggler-wrapper`} id="nav-toggler">
6574
<NavToggler className={`${baseClass}__nav-toggler`}>
6675
<NavHamburger />

packages/payload/src/bin/generateImportMap/iterateConfig.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@ export function iterateConfig({
3636
imports,
3737
})
3838

39-
typeof config.admin?.avatar === 'object' && addToImportMap(config.admin?.avatar?.Component)
39+
if (typeof config.admin?.avatar === 'object') {
40+
addToImportMap(config.admin?.avatar?.Component)
41+
}
4042

4143
addToImportMap(config.admin?.components?.Nav)
42-
44+
addToImportMap(config.admin?.components?.header)
4345
addToImportMap(config.admin?.components?.logout?.Button)
4446
addToImportMap(config.admin?.components?.graphics?.Icon)
4547
addToImportMap(config.admin?.components?.graphics?.Logo)

packages/payload/src/config/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,10 @@ export type Config = {
697697
/** Replace the logo on the login page */
698698
Logo?: CustomComponent
699699
}
700+
/**
701+
* Add custom header to top of page globally
702+
*/
703+
header?: CustomComponent[]
700704
/** Replace logout related components */
701705
logout?: {
702706
/** Replace the logout button */
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { PayloadServerReactComponent, SanitizedConfig } from 'payload'
2+
3+
import React from 'react'
4+
5+
const baseClass = 'custom-header'
6+
7+
export const CustomHeader: PayloadServerReactComponent<
8+
SanitizedConfig['admin']['components']['header'][0]
9+
> = () => {
10+
return (
11+
<div
12+
className={baseClass}
13+
style={{
14+
alignItems: 'center',
15+
backgroundColor: 'var(--theme-success-500)',
16+
display: 'flex',
17+
minHeight: 'var(--app-header-height)',
18+
padding: '0 var(--gutter-h)',
19+
// position: 'sticky',
20+
top: 0,
21+
width: '100%',
22+
zIndex: 'var(--z-modal)',
23+
}}
24+
>
25+
<p style={{ color: 'var(--theme-text)', margin: 0 }}>
26+
Here is a custom header inserted with admin.components.header
27+
</p>
28+
</div>
29+
)
30+
}

test/admin/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export default buildConfigWithDefaults({
4747
],
4848
afterNavLinks: ['/components/AfterNavLinks/index.js#AfterNavLinks'],
4949
beforeLogin: ['/components/BeforeLogin/index.js#BeforeLogin'],
50+
header: ['/components/CustomHeader/index.js#CustomHeader'],
5051
logout: {
5152
Button: '/components/Logout/index.js#Logout',
5253
},

test/admin/e2e/1/e2e.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,14 @@ describe('admin1', () => {
680680
})
681681
})
682682

683+
describe('custom components', () => {
684+
test('should render custom header', async () => {
685+
await page.goto(`${serverURL}/admin`)
686+
const header = page.locator('.custom-header')
687+
await expect(header).toContainText('Here is a custom header')
688+
})
689+
})
690+
683691
describe('API view', () => {
684692
test('collection — should not show API tab when disabled in config', async () => {
685693
await page.goto(postsUrl.collection(noApiViewCollectionSlug))

0 commit comments

Comments
 (0)