Skip to content

Commit 7438812

Browse files
committed
feat!: replaces admin.favicon with admin.icons
1 parent a48043c commit 7438812

File tree

16 files changed

+97
-37
lines changed

16 files changed

+97
-37
lines changed

packages/next/src/layouts/Root/index.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ const merriweather = Merriweather({
2626
weight: ['400', '900'],
2727
})
2828

29+
export const metadata = {
30+
description: 'Generated by Next.js',
31+
title: 'Next.js',
32+
}
33+
2934
export const RootLayout = async ({
3035
children,
3136
config: configPromise,

packages/next/src/utilities/meta.ts

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Metadata } from 'next'
2+
import type { Icon } from 'next/dist/lib/metadata/types/metadata-types.js'
23
import type { SanitizedConfig } from 'payload/types'
34

45
import { payloadFaviconDark, payloadFaviconLight, payloadOgImage } from '@payloadcms/ui/assets'
@@ -13,38 +14,35 @@ export const meta = async (args: {
1314

1415
const titleSuffix = config.admin.meta?.titleSuffix ?? '- Payload'
1516

16-
const customFavicon = config.admin.meta?.favicon
17-
const customFaviconFiletype = customFavicon?.split('.').pop()
18-
const customFaviconMediaType = `image/${customFaviconFiletype}`
19-
20-
const favicon = customFavicon ?? payloadFaviconLight?.src
2117
const ogImage = config.admin?.meta?.ogImage ?? payloadOgImage?.src
2218

19+
const customIcons = config.admin.meta.icons as Metadata['icons']
20+
21+
let icons = customIcons ?? []
22+
23+
const payloadIcons: Icon[] = [
24+
{
25+
type: 'image/png',
26+
rel: 'icon',
27+
sizes: '32x32',
28+
url: payloadFaviconDark?.src,
29+
},
30+
{
31+
type: 'image/png',
32+
media: '(prefers-color-scheme: dark)',
33+
rel: 'icon',
34+
sizes: '32x32',
35+
url: payloadFaviconLight?.src,
36+
},
37+
]
38+
39+
if (customIcons && typeof customIcons === 'object' && Array.isArray(customIcons)) {
40+
icons = payloadIcons.concat(customIcons)
41+
}
42+
2343
return Promise.resolve({
2444
description,
25-
icons: [
26-
...(customFavicon
27-
? [
28-
{
29-
type: customFaviconMediaType,
30-
rel: 'icon',
31-
url: favicon,
32-
},
33-
]
34-
: [
35-
{
36-
type: 'image/png',
37-
rel: 'icon',
38-
url: payloadFaviconDark?.src,
39-
},
40-
{
41-
type: 'image/png',
42-
media: '(prefers-color-scheme: dark)',
43-
rel: 'icon',
44-
url: payloadFaviconLight?.src,
45-
},
46-
]),
47-
],
45+
icons,
4846
keywords,
4947
metadataBase: new URL(
5048
config?.serverURL ||

packages/payload/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@
149149
"get-port": "5.1.1",
150150
"graphql-http": "^1.22.0",
151151
"mini-css-extract-plugin": "1.6.2",
152+
"next": "^14.3.0-canary.7",
152153
"nodemon": "3.0.3",
153154
"object.assign": "4.1.4",
154155
"object.entries": "1.1.6",

packages/payload/src/config/schema.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,13 @@ export default joi.object({
6868
}),
6969
logoutRoute: joi.string(),
7070
meta: joi.object().keys({
71-
favicon: joi.string(),
71+
icons: joi
72+
.alternatives()
73+
.try(
74+
joi.array().items(joi.alternatives().try(joi.string(), joi.object())),
75+
joi.object(),
76+
joi.string().allow(null),
77+
),
7278
ogImage: joi.string(),
7379
titleSuffix: joi.string(),
7480
}),

packages/payload/src/config/types.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { I18nOptions, TFunction } from '@payloadcms/translations'
22
import type { Options as ExpressFileUploadOptions } from 'express-fileupload'
33
import type GraphQL from 'graphql'
4+
import type { Metadata as NextMetadata } from 'next'
45
import type { DestinationStream, LoggerOptions, P } from 'pino'
56
import type React from 'react'
67
import type { default as sharp } from 'sharp'
@@ -460,14 +461,15 @@ export type Config = {
460461
}
461462
/** The route for the logout page. */
462463
logoutRoute?: string
463-
/** Base meta data to use for the Admin panel. Included properties are titleSuffix, ogImage, and favicon. */
464+
/** Base meta data to use for the Admin Panel. Included properties are titleSuffix, ogImage, and favicon. */
464465
meta?: {
465466
/**
466-
* Public path to an icon
467+
* An array of Next.js metadata objects that represent icons to be used by devices and browsers.
467468
*
468-
* This image may be displayed in the browser next to the title of the page
469+
* For example browser tabs, phone home screens, and search engine results.
470+
* @reference https://nextjs.org/docs/app/api-reference/functions/generate-metadata#icons
469471
*/
470-
favicon?: string
472+
icons?: NextMetadata['icons']
471473
/**
472474
* Public path to an image
473475
*

packages/ui/src/assets/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export { default as payloadFavicon } from '../assets/favicon.svg'
2-
export { default as payloadFaviconDark } from '../assets/favicon-dark.png'
3-
export { default as payloadFaviconLight } from '../assets/favicon-light.png'
41
export { default as payloadOgImage } from '../assets/og-image.png'
2+
export { default as payloadFavicon } from '../assets/payload-favicon.svg'
3+
export { default as payloadFaviconDark } from '../assets/payload-favicon-dark.png'
4+
export { default as payloadFaviconLight } from '../assets/payload-favicon-light.png'

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/_community/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { devUser } from '../credentials.js'
77
import { MediaCollection } from './collections/Media/index.js'
88
import { PostsCollection, postsSlug } from './collections/Posts/index.js'
99
import { MenuGlobal } from './globals/Menu/index.js'
10+
1011
const filename = fileURLToPath(import.meta.url)
1112
const dirname = path.dirname(filename)
1213

test/admin/config.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import { CustomMinimalView } from './components/views/CustomMinimal/index.js'
2525
import { CustomView } from './components/views/CustomView/index.js'
2626
import { CustomNestedView } from './components/views/CustomViewNested/index.js'
2727
import { CustomViewWithParam } from './components/views/CustomViewWithParam/index.js'
28+
import { default as customFaviconDark } from './custom-favicon-dark.png'
29+
import { default as customFaviconLight } from './custom-favicon-light.png'
2830
import { CustomGlobalViews1 } from './globals/CustomViews1.js'
2931
import { CustomGlobalViews2 } from './globals/CustomViews2.js'
3032
import { Global } from './globals/Global.js'
@@ -34,7 +36,6 @@ import { GlobalHidden } from './globals/Hidden.js'
3436
import { GlobalNoApiView } from './globals/NoApiView.js'
3537
import { seed } from './seed.js'
3638
import { customNestedViewPath, customParamViewPath, customViewPath } from './shared.js'
37-
3839
export default buildConfigWithDefaults({
3940
admin: {
4041
components: {
@@ -74,6 +75,21 @@ export default buildConfigWithDefaults({
7475
},
7576
},
7677
},
78+
meta: {
79+
icons: [
80+
{
81+
type: 'image/png',
82+
rel: 'icon',
83+
url: customFaviconDark.src,
84+
},
85+
{
86+
type: 'image/png',
87+
media: '(prefers-color-scheme: dark)',
88+
rel: 'icon',
89+
url: customFaviconLight.src,
90+
},
91+
],
92+
},
7793
},
7894
collections: [
7995
UploadCollection,

test/admin/custom-favicon-dark.png

477 Bytes
Loading

test/admin/custom-favicon-light.png

496 Bytes
Loading

test/admin/e2e.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,30 @@ describe('admin', () => {
106106
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
107107
})
108108

109+
describe('metadata', () => {
110+
test('should set Payload favicons', async () => {
111+
await page.goto(postsUrl.admin)
112+
const favicons = page.locator('link[rel="icon"]')
113+
await expect(favicons).toHaveCount(4)
114+
await expect(favicons.nth(0)).toHaveAttribute('sizes', '32x32')
115+
await expect(favicons.nth(1)).toHaveAttribute('sizes', '32x32')
116+
await expect(favicons.nth(1)).toHaveAttribute('media', '(prefers-color-scheme: dark)')
117+
await expect(favicons.nth(1)).toHaveAttribute(
118+
'href',
119+
/\/payload-favicon-light\.[a-z\d]+\.png/,
120+
)
121+
})
122+
123+
test('should inject custom favicons', async () => {
124+
await page.goto(postsUrl.admin)
125+
const favicons = page.locator('link[rel="icon"]')
126+
await expect(favicons).toHaveCount(4)
127+
await expect(favicons.nth(2)).toHaveAttribute('href', /\/custom-favicon-dark\.[a-z\d]+\.png/)
128+
await expect(favicons.nth(3)).toHaveAttribute('media', '(prefers-color-scheme: dark)')
129+
await expect(favicons.nth(3)).toHaveAttribute('href', /\/custom-favicon-light\.[a-z\d]+\.png/)
130+
})
131+
})
132+
109133
describe('navigation', () => {
110134
test('nav — should navigate to collection', async () => {
111135
await page.goto(postsUrl.admin)

test/admin/types.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
declare module '*.png' {
2+
const value: any
3+
export = value
4+
}

0 commit comments

Comments
 (0)