Skip to content

Commit f23a1df

Browse files
feat: adds beforeNav and afterNav component slots (#15493)
Allows custom components to be slotted in before the nav, and not specifically beforeNavLinks. `beforeNavLinks` semantically means before the links themselves, but for a PR like #15470, we will want to be able to place things outside of the nav links section. Think about the multi-tenant selector, you would not want that visible in just the first tab, but outside of the tabs - i.e. before the nav.
1 parent 63d63be commit f23a1df

File tree

9 files changed

+292
-81
lines changed

9 files changed

+292
-81
lines changed

packages/next/src/elements/Nav/index.tsx

Lines changed: 79 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export const DefaultNav: React.FC<NavProps> = async (props) => {
4141

4242
const {
4343
admin: {
44-
components: { afterNavLinks, beforeNavLinks, logout, settingsMenu },
44+
components: { afterNav, afterNavLinks, beforeNav, beforeNavLinks, logout, settingsMenu },
4545
},
4646
collections,
4747
globals,
@@ -93,7 +93,7 @@ export const DefaultNav: React.FC<NavProps> = async (props) => {
9393
},
9494
})
9595

96-
const renderedSettingsMenu =
96+
const RenderedSettingsMenu =
9797
settingsMenu && Array.isArray(settingsMenu)
9898
? settingsMenu.map((item, index) =>
9999
RenderServerComponent({
@@ -117,49 +117,91 @@ export const DefaultNav: React.FC<NavProps> = async (props) => {
117117
)
118118
: []
119119

120+
const RenderedBeforeNav = RenderServerComponent({
121+
clientProps: {
122+
documentSubViewType,
123+
viewType,
124+
},
125+
Component: beforeNav,
126+
importMap: payload.importMap,
127+
serverProps: {
128+
i18n,
129+
locale,
130+
params,
131+
payload,
132+
permissions,
133+
searchParams,
134+
user,
135+
},
136+
})
137+
138+
const RenderedBeforeNavLinks = RenderServerComponent({
139+
clientProps: {
140+
documentSubViewType,
141+
viewType,
142+
},
143+
Component: beforeNavLinks,
144+
importMap: payload.importMap,
145+
serverProps: {
146+
i18n,
147+
locale,
148+
params,
149+
payload,
150+
permissions,
151+
searchParams,
152+
user,
153+
},
154+
})
155+
156+
const RenderedAfterNavLinks = RenderServerComponent({
157+
clientProps: {
158+
documentSubViewType,
159+
viewType,
160+
},
161+
Component: afterNavLinks,
162+
importMap: payload.importMap,
163+
serverProps: {
164+
i18n,
165+
locale,
166+
params,
167+
payload,
168+
permissions,
169+
searchParams,
170+
user,
171+
},
172+
})
173+
174+
const RenderedAfterNav = RenderServerComponent({
175+
clientProps: {
176+
documentSubViewType,
177+
viewType,
178+
},
179+
Component: afterNav,
180+
importMap: payload.importMap,
181+
serverProps: {
182+
i18n,
183+
locale,
184+
params,
185+
payload,
186+
permissions,
187+
searchParams,
188+
user,
189+
},
190+
})
191+
120192
return (
121193
<NavWrapper baseClass={baseClass}>
194+
{RenderedBeforeNav}
122195
<nav className={`${baseClass}__wrap`}>
123-
{RenderServerComponent({
124-
clientProps: {
125-
documentSubViewType,
126-
viewType,
127-
},
128-
Component: beforeNavLinks,
129-
importMap: payload.importMap,
130-
serverProps: {
131-
i18n,
132-
locale,
133-
params,
134-
payload,
135-
permissions,
136-
searchParams,
137-
user,
138-
},
139-
})}
196+
{RenderedBeforeNavLinks}
140197
<DefaultNavClient groups={groups} navPreferences={navPreferences} />
141-
{RenderServerComponent({
142-
clientProps: {
143-
documentSubViewType,
144-
viewType,
145-
},
146-
Component: afterNavLinks,
147-
importMap: payload.importMap,
148-
serverProps: {
149-
i18n,
150-
locale,
151-
params,
152-
payload,
153-
permissions,
154-
searchParams,
155-
user,
156-
},
157-
})}
198+
{RenderedAfterNavLinks}
158199
<div className={`${baseClass}__controls`}>
159-
<SettingsMenuButton settingsMenu={renderedSettingsMenu} />
200+
<SettingsMenuButton settingsMenu={RenderedSettingsMenu} />
160201
{LogoutComponent}
161202
</div>
162203
</nav>
204+
{RenderedAfterNav}
163205
<div className={`${baseClass}__header`}>
164206
<div className={`${baseClass}__header-content`}>
165207
<NavHamburger baseClass={baseClass} />

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,11 @@ export function iterateConfig({
6464
addToImportMap(config.admin?.components?.actions)
6565
addToImportMap(config.admin?.components?.afterDashboard)
6666
addToImportMap(config.admin?.components?.afterLogin)
67+
addToImportMap(config.admin?.components?.afterNav)
6768
addToImportMap(config.admin?.components?.afterNavLinks)
6869
addToImportMap(config.admin?.components?.beforeDashboard)
6970
addToImportMap(config.admin?.components?.beforeLogin)
71+
addToImportMap(config.admin?.components?.beforeNav)
7072
addToImportMap(config.admin?.components?.beforeNavLinks)
7173

7274
addToImportMap(config.admin?.components?.providers)

packages/payload/src/config/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,10 @@ export type Config = {
851851
* Add custom components after the email/password field
852852
*/
853853
afterLogin?: CustomComponent[]
854+
/**
855+
* Add custom components after the navigation section
856+
*/
857+
afterNav?: CustomComponent[]
854858
/**
855859
* Add custom components after the navigation links
856860
*/
@@ -863,6 +867,10 @@ export type Config = {
863867
* Add custom components before the email/password field
864868
*/
865869
beforeLogin?: CustomComponent[]
870+
/**
871+
* Add custom components before the navigation section
872+
*/
873+
beforeNav?: CustomComponent[]
866874
/**
867875
* Add custom components before the navigation links
868876
*/
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use client'
2+
3+
import type { PayloadClientReactComponent, SanitizedConfig } from 'payload'
4+
5+
import React from 'react'
6+
7+
const baseClass = 'after-nav'
8+
9+
export const AfterNav: PayloadClientReactComponent<
10+
SanitizedConfig['admin']['components']['afterNav'][0]
11+
> = () => {
12+
return (
13+
<div
14+
className={baseClass}
15+
id="after-nav-component"
16+
style={{
17+
backgroundColor: 'var(--theme-success-100)',
18+
borderRadius: 'var(--style-radius-m)',
19+
color: 'var(--theme-success-750)',
20+
marginTop: 'var(--base)',
21+
padding: 'calc(var(--base) * 0.5)',
22+
}}
23+
>
24+
<p style={{ margin: 0 }}>afterNav</p>
25+
</div>
26+
)
27+
}

test/admin/components/AfterNavLinks/index.tsx

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
import type { PayloadClientReactComponent, SanitizedConfig } from 'payload'
44

5-
import LinkImport from 'next/link.js'
6-
const Link = 'default' in LinkImport ? LinkImport.default : LinkImport
7-
85
import { useConfig } from '@payloadcms/ui'
6+
import LinkImport from 'next/link.js'
97
import React from 'react'
108

9+
const Link = 'default' in LinkImport ? LinkImport.default : LinkImport
10+
1111
const baseClass = 'after-nav-links'
1212

1313
export const AfterNavLinks: PayloadClientReactComponent<
@@ -22,26 +22,47 @@ export const AfterNavLinks: PayloadClientReactComponent<
2222
return (
2323
<div
2424
className={baseClass}
25+
id="after-nav-links-component"
2526
style={{
27+
backgroundColor: 'var(--theme-success-100)',
28+
borderRadius: 'var(--style-radius-m)',
29+
color: 'var(--theme-success-750)',
2630
display: 'flex',
2731
flexDirection: 'column',
28-
gap: 'calc(var(--base) / 4)',
32+
marginTop: 'var(--base)',
33+
padding: 'var(--base)',
34+
width: '100%',
2935
}}
3036
>
31-
<h4 className="nav__label" style={{ color: 'var(--theme-elevation-400)', margin: 0 }}>
32-
Custom Routes
33-
</h4>
34-
<h4 className="nav__link" style={{ margin: 0 }}>
35-
<Link href={`${adminRoute}/custom-default-view`} style={{ textDecoration: 'none' }}>
36-
Default Template
37-
</Link>
38-
</h4>
39-
<h4 className="nav__link" style={{ margin: 0 }}>
40-
<Link href={`${adminRoute}/custom-minimal-view`} style={{ textDecoration: 'none' }}>
41-
Minimal Template
42-
</Link>
43-
</h4>
44-
<div id="custom-css" />
37+
<p style={{ marginBottom: '16px', marginTop: 0 }}>afterNavLinks</p>
38+
<div
39+
style={{
40+
display: 'flex',
41+
flexDirection: 'column',
42+
gap: '5px',
43+
}}
44+
>
45+
<p className="nav__label" style={{ color: '#1565c0', margin: 0 }}>
46+
Custom Routes
47+
</p>
48+
<p className="nav__link" style={{ margin: 0 }}>
49+
<Link
50+
href={`${adminRoute}/custom-default-view`}
51+
style={{ color: '#1976d2', textDecoration: 'none' }}
52+
>
53+
Default Template
54+
</Link>
55+
</p>
56+
<p className="nav__link" style={{ margin: 0 }}>
57+
<Link
58+
href={`${adminRoute}/custom-minimal-view`}
59+
style={{ color: '#1976d2', textDecoration: 'none' }}
60+
>
61+
Minimal Template
62+
</Link>
63+
</p>
64+
<div id="custom-css" />
65+
</div>
4566
</div>
4667
)
4768
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use client'
2+
3+
import type { PayloadClientReactComponent, SanitizedConfig } from 'payload'
4+
5+
import React from 'react'
6+
7+
const baseClass = 'before-nav'
8+
9+
export const BeforeNav: PayloadClientReactComponent<
10+
SanitizedConfig['admin']['components']['beforeNav'][0]
11+
> = () => {
12+
return (
13+
<div
14+
className={baseClass}
15+
id="before-nav-component"
16+
style={{
17+
backgroundColor: 'var(--theme-success-100)',
18+
borderRadius: 'var(--style-radius-m)',
19+
color: 'var(--theme-success-750)',
20+
marginBottom: 'var(--base)',
21+
padding: 'calc(var(--base) * 0.5)',
22+
}}
23+
>
24+
<p style={{ margin: 0 }}>beforeNav</p>
25+
</div>
26+
)
27+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use client'
2+
3+
import type { PayloadClientReactComponent, SanitizedConfig } from 'payload'
4+
5+
import React from 'react'
6+
7+
const baseClass = 'before-nav-links'
8+
9+
export const BeforeNavLinks: PayloadClientReactComponent<
10+
SanitizedConfig['admin']['components']['beforeNavLinks'][0]
11+
> = () => {
12+
return (
13+
<div
14+
className={baseClass}
15+
id="before-nav-links-component"
16+
style={{
17+
backgroundColor: 'var(--theme-success-100)',
18+
borderRadius: 'var(--style-radius-m)',
19+
color: 'var(--theme-success-750)',
20+
marginBottom: 'var(--base)',
21+
padding: 'calc(var(--base) * 0.5)',
22+
width: '100%',
23+
}}
24+
>
25+
beforeNavLinks
26+
</div>
27+
)
28+
}

0 commit comments

Comments
 (0)