Compare commits
6 Commits
fix/tab-la
...
v3.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e900e8974b | ||
|
|
adc9bb5cbd | ||
|
|
a09811f5d4 | ||
|
|
c73f6c74b3 | ||
|
|
5c2e39ef0c | ||
|
|
84d2026330 |
@@ -98,6 +98,7 @@ The following options are available:
|
||||
| **`livePreview`** | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
|
||||
| **`meta`** | Base metadata to use for the Admin Panel. [More details](./metadata). |
|
||||
| **`routes`** | Replace built-in Admin Panel routes with your own custom routes. [More details](#customizing-routes). |
|
||||
| **`theme`** | Restrict the Admin Panel theme to use only one of your choice. Default is `all`.
|
||||
| **`user`** | The `slug` of the Collection that you want to allow to login to the Admin Panel. [More details](#the-admin-user-collection). |
|
||||
|
||||
<Banner type="success">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload-monorepo",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-payload-app",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "The officially supported MongoDB database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import ObjectIdImport from 'bson-objectid'
|
||||
import { isValidObjectId } from 'mongoose'
|
||||
import {
|
||||
buildVersionCollectionFields,
|
||||
type CreateVersion,
|
||||
@@ -10,6 +12,8 @@ import type { MongooseAdapter } from './index.js'
|
||||
import { sanitizeRelationshipIDs } from './utilities/sanitizeRelationshipIDs.js'
|
||||
import { withSession } from './withSession.js'
|
||||
|
||||
const ObjectId = (ObjectIdImport.default ||
|
||||
ObjectIdImport) as unknown as typeof ObjectIdImport.default
|
||||
export const createVersion: CreateVersion = async function createVersion(
|
||||
this: MongooseAdapter,
|
||||
{
|
||||
@@ -47,6 +51,23 @@ export const createVersion: CreateVersion = async function createVersion(
|
||||
|
||||
const [doc] = await VersionModel.create([data], options, req)
|
||||
|
||||
const parentQuery = {
|
||||
$or: [
|
||||
{
|
||||
parent: {
|
||||
$eq: data.parent,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
if (typeof data.parent === 'string' && isValidObjectId(data.parent)) {
|
||||
parentQuery.$or.push({
|
||||
parent: {
|
||||
$eq: ObjectId(data.parent),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
await VersionModel.updateMany(
|
||||
{
|
||||
$and: [
|
||||
@@ -55,11 +76,7 @@ export const createVersion: CreateVersion = async function createVersion(
|
||||
$ne: doc._id,
|
||||
},
|
||||
},
|
||||
{
|
||||
parent: {
|
||||
$eq: data.parent,
|
||||
},
|
||||
},
|
||||
parentQuery,
|
||||
{
|
||||
latest: {
|
||||
$eq: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-sqlite",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "The officially supported SQLite database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-vercel-postgres",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "Vercel Postgres adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/drizzle",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "A library of shared functions used by different payload database adapters",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-nodemailer",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "Payload Nodemailer Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-resend",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "Payload Resend Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/graphql",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview-react",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "The official React SDK for Payload Live Preview",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview-vue",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "The official Vue SDK for Payload Live Preview",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "The official live preview JavaScript SDK for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/next",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -12,6 +12,10 @@ type GetRequestLanguageArgs = {
|
||||
const acceptedThemes: Theme[] = ['dark', 'light']
|
||||
|
||||
export const getRequestTheme = ({ config, cookies, headers }: GetRequestLanguageArgs): Theme => {
|
||||
if (config.admin.theme !== 'all' && acceptedThemes.includes(config.admin.theme)) {
|
||||
return config.admin.theme
|
||||
}
|
||||
|
||||
const themeCookie = cookies.get(`${config.cookiePrefix || 'payload'}-theme`)
|
||||
|
||||
const themeFromCookie: Theme = (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { I18n } from '@payloadcms/translations'
|
||||
import type { LanguageOptions } from 'payload'
|
||||
import type { Config, LanguageOptions } from 'payload'
|
||||
|
||||
import { FieldLabel } from '@payloadcms/ui'
|
||||
import React from 'react'
|
||||
@@ -14,8 +14,9 @@ export const Settings: React.FC<{
|
||||
readonly className?: string
|
||||
readonly i18n: I18n
|
||||
readonly languageOptions: LanguageOptions
|
||||
readonly theme: Config['admin']['theme']
|
||||
}> = (props) => {
|
||||
const { className, i18n, languageOptions } = props
|
||||
const { className, i18n, languageOptions, theme } = props
|
||||
|
||||
return (
|
||||
<div className={[baseClass, className].filter(Boolean).join(' ')}>
|
||||
@@ -24,7 +25,7 @@ export const Settings: React.FC<{
|
||||
<FieldLabel field={null} htmlFor="language-select" label={i18n.t('general:language')} />
|
||||
<LanguageSelector languageOptions={languageOptions} />
|
||||
</div>
|
||||
<ToggleTheme />
|
||||
{theme === 'all' && <ToggleTheme />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,11 @@ export const Account: React.FC<AdminViewProps> = async ({
|
||||
} = initPageResult
|
||||
|
||||
const {
|
||||
admin: { components: { views: { Account: CustomAccountComponent } = {} } = {}, user: userSlug },
|
||||
admin: {
|
||||
components: { views: { Account: CustomAccountComponent } = {} } = {},
|
||||
theme,
|
||||
user: userSlug,
|
||||
},
|
||||
routes: { api },
|
||||
serverURL,
|
||||
} = config
|
||||
@@ -85,7 +89,7 @@ export const Account: React.FC<AdminViewProps> = async ({
|
||||
|
||||
return (
|
||||
<DocumentInfoProvider
|
||||
AfterFields={<Settings i18n={i18n} languageOptions={languageOptions} />}
|
||||
AfterFields={<Settings i18n={i18n} languageOptions={languageOptions} theme={theme} />}
|
||||
apiURL={`${serverURL}${api}/${userSlug}${user?.id ? `/${user.id}` : ''}`}
|
||||
collectionSlug={userSlug}
|
||||
docPermissions={docPermissions}
|
||||
|
||||
@@ -113,10 +113,19 @@ export const DefaultEditView: React.FC = () => {
|
||||
|
||||
const isLockingEnabled = lockDocumentsProp !== false
|
||||
|
||||
const preventLeaveWithoutSaving =
|
||||
(!(collectionConfig?.versions?.drafts && collectionConfig?.versions?.drafts?.autosave) ||
|
||||
!(globalConfig?.versions?.drafts && globalConfig?.versions?.drafts?.autosave)) &&
|
||||
!disableLeaveWithoutSaving
|
||||
let preventLeaveWithoutSaving = true
|
||||
|
||||
if (collectionConfig) {
|
||||
preventLeaveWithoutSaving = !(
|
||||
collectionConfig?.versions?.drafts && collectionConfig?.versions?.drafts?.autosave
|
||||
)
|
||||
} else if (globalConfig) {
|
||||
preventLeaveWithoutSaving = !(
|
||||
globalConfig?.versions?.drafts && globalConfig?.versions?.drafts?.autosave
|
||||
)
|
||||
} else if (typeof disableLeaveWithoutSaving !== 'undefined') {
|
||||
preventLeaveWithoutSaving = !disableLeaveWithoutSaving
|
||||
}
|
||||
|
||||
const [isReadOnlyForIncomingUser, setIsReadOnlyForIncomingUser] = useState(false)
|
||||
const [showTakeOverModal, setShowTakeOverModal] = useState(false)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "Node, React, Headless CMS and Application Framework built on Next.js",
|
||||
"keywords": [
|
||||
"admin panel",
|
||||
|
||||
@@ -42,12 +42,6 @@ export type DocumentTabConfig = {
|
||||
readonly Pill?: PayloadComponent
|
||||
}
|
||||
|
||||
export type ClientDocumentTabConfig = {
|
||||
condition?: never
|
||||
isActive?: boolean
|
||||
label?: string
|
||||
} & DocumentTabConfig
|
||||
|
||||
export type DocumentTabComponent = PayloadComponent<{
|
||||
path: string
|
||||
}>
|
||||
|
||||
@@ -11,7 +11,6 @@ export type { CustomPublishButton } from './elements/PublishButton.js'
|
||||
export type { CustomSaveButton } from './elements/SaveButton.js'
|
||||
export type { CustomSaveDraftButton } from './elements/SaveDraftButton.js'
|
||||
export type {
|
||||
ClientDocumentTabConfig,
|
||||
DocumentTabComponent,
|
||||
DocumentTabCondition,
|
||||
DocumentTabConfig,
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { Locale, MetaConfig, PayloadComponent } from '../../config/types.js
|
||||
import type { SanitizedGlobalConfig } from '../../globals/config/types.js'
|
||||
import type { PayloadRequest } from '../../types/index.js'
|
||||
import type { LanguageOptions } from '../LanguageOptions.js'
|
||||
import type { ClientDocumentTabConfig, MappedComponent } from '../types.js'
|
||||
import type { MappedComponent } from '../types.js'
|
||||
|
||||
export type AdminViewConfig = {
|
||||
Component: AdminViewComponent
|
||||
@@ -23,7 +23,6 @@ export type AdminViewConfig = {
|
||||
export type MappedView = {
|
||||
actions?: MappedComponent[]
|
||||
Component: MappedComponent
|
||||
tab?: ClientDocumentTabConfig
|
||||
}
|
||||
|
||||
export type AdminViewProps = {
|
||||
|
||||
@@ -25,6 +25,7 @@ export const defaults: Omit<Config, 'db' | 'editor' | 'secret'> = {
|
||||
reset: '/reset',
|
||||
unauthorized: '/unauthorized',
|
||||
},
|
||||
theme: 'all',
|
||||
},
|
||||
bin: [],
|
||||
collections: [],
|
||||
|
||||
@@ -818,6 +818,12 @@ export type Config = {
|
||||
/** The route for the unauthorized page. */
|
||||
unauthorized?: string
|
||||
}
|
||||
/**
|
||||
* Restrict the Admin Panel theme to use only one of your choice
|
||||
*
|
||||
* @default 'all' // The theme can be configured by users
|
||||
*/
|
||||
theme?: 'all' | 'dark' | 'light'
|
||||
/** The slug of a Collection that you want to be used to log in to the Admin dashboard. */
|
||||
user?: string
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-cloud-storage",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "The official cloud storage plugin for Payload CMS",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-cloud",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "The official Payload Cloud plugin",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-form-builder",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "Form builder plugin for Payload CMS",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-nested-docs",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "The official Nested Docs plugin for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-redirects",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "Redirects plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-search",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "Search plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-seo",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "SEO plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-stripe",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "Stripe plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/richtext-lexical",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "The officially supported Lexical richtext adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
.LexicalEditorTheme {
|
||||
&__table {
|
||||
border-collapse: collapse;
|
||||
max-width: 100%;
|
||||
border-spacing: 0;
|
||||
overflow-y: scroll;
|
||||
overflow-x: scroll;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/richtext-slate",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "The officially supported Slate richtext adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-azure",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "Payload storage adapter for Azure Blob Storage",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-gcs",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "Payload storage adapter for Google Cloud Storage",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-s3",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "Payload storage adapter for Amazon S3",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-uploadthing",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "Payload storage adapter for uploadthing",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-vercel-blob",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"description": "Payload storage adapter for Vercel Blob Storage",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/translations",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/ui",
|
||||
"version": "3.0.0-beta.108",
|
||||
"version": "3.0.0-beta.109",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -47,7 +47,7 @@ export const Autosave: React.FC<Props> = ({
|
||||
} = useConfig()
|
||||
const { docConfig, getVersions, versions } = useDocumentInfo()
|
||||
const { reportUpdate } = useDocumentEvents()
|
||||
const { dispatchFields, setModified, setSubmitted } = useForm()
|
||||
const { dispatchFields, setSubmitted } = useForm()
|
||||
const submitted = useFormSubmitted()
|
||||
const versionsConfig = docConfig?.versions
|
||||
|
||||
@@ -149,7 +149,7 @@ export const Autosave: React.FC<Props> = ({
|
||||
entitySlug,
|
||||
updatedAt: newDate.toISOString(),
|
||||
})
|
||||
setModified(false)
|
||||
|
||||
void getVersions()
|
||||
} else {
|
||||
return res.json()
|
||||
@@ -247,7 +247,6 @@ export const Autosave: React.FC<Props> = ({
|
||||
reportUpdate,
|
||||
serverURL,
|
||||
setSubmitted,
|
||||
setModified,
|
||||
versionsConfig?.drafts,
|
||||
debouncedFields,
|
||||
submitted,
|
||||
|
||||
@@ -78,7 +78,7 @@ const NumberFieldComponent: NumberFieldClientComponent = (props) => {
|
||||
let newVal = val
|
||||
|
||||
if (Number.isNaN(val)) {
|
||||
newVal = undefined
|
||||
newVal = null
|
||||
}
|
||||
|
||||
if (typeof onChangeFromProps === 'function') {
|
||||
|
||||
@@ -250,6 +250,15 @@ export const createClientCollectionConfig = ({
|
||||
)
|
||||
}
|
||||
|
||||
// Ensure that the label in Edit view is a string, translate it if it's a function
|
||||
if (collection?.admin?.components?.views?.edit) {
|
||||
Object.entries(collection?.admin?.components?.views?.edit).forEach(([key, view]) => {
|
||||
if ('tab' in view && 'label' in view.tab && typeof view.tab.label === 'function') {
|
||||
collection.admin.components.views.edit[key].tab.label = view.tab.label({ t: i18n.t })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
clientCollection.admin.components.views = (
|
||||
collection?.admin?.components?.views
|
||||
? deepCopyObjectSimple(collection?.admin?.components?.views)
|
||||
@@ -306,13 +315,6 @@ export const createClientCollectionConfig = ({
|
||||
)
|
||||
}
|
||||
|
||||
// Ensure that the label in a tab in an Edit view is a string, translate it if it's a function
|
||||
if ('tab' in view && 'label' in view.tab && typeof view.tab.label === 'function') {
|
||||
clientCollection.admin.components.views.edit[key].tab.label = view.tab.label({
|
||||
t: i18n.t,
|
||||
})
|
||||
}
|
||||
|
||||
if ('actions' in view && view.actions?.length) {
|
||||
clientCollection.admin.components.views.edit[key].actions = view.actions.map((Component) =>
|
||||
createMappedComponent(
|
||||
|
||||
@@ -88,7 +88,7 @@ export const RootProvider: React.FC<Props> = ({
|
||||
<ModalProvider classPrefix="payload" transTime={0} zIndex="var(--z-modal)">
|
||||
<AuthProvider permissions={permissions} user={user}>
|
||||
<PreferencesProvider>
|
||||
<ThemeProvider cookiePrefix={config.cookiePrefix} theme={theme}>
|
||||
<ThemeProvider theme={theme}>
|
||||
<ParamsProvider>
|
||||
<LocaleProvider>
|
||||
<StepNavProvider>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
'use client'
|
||||
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'
|
||||
|
||||
import { useConfig } from '../Config/index.js'
|
||||
|
||||
export type Theme = 'dark' | 'light'
|
||||
|
||||
export type ThemeContext = {
|
||||
@@ -55,20 +57,26 @@ export const defaultTheme = 'light'
|
||||
|
||||
export const ThemeProvider: React.FC<{
|
||||
children?: React.ReactNode
|
||||
cookiePrefix?: string
|
||||
theme?: Theme
|
||||
}> = ({ children, cookiePrefix, theme: initialTheme }) => {
|
||||
const cookieKey = `${cookiePrefix || 'payload'}-theme`
|
||||
}> = ({ children, theme: initialTheme }) => {
|
||||
const { config } = useConfig()
|
||||
|
||||
const preselectedTheme = config.admin.theme
|
||||
const cookieKey = `${config.cookiePrefix || 'payload'}-theme`
|
||||
|
||||
const [theme, setThemeState] = useState<Theme>(initialTheme || defaultTheme)
|
||||
|
||||
const [autoMode, setAutoMode] = useState<boolean>()
|
||||
|
||||
useEffect(() => {
|
||||
if (preselectedTheme !== 'all') {
|
||||
return
|
||||
}
|
||||
|
||||
const { theme, themeFromCookies } = getTheme(cookieKey)
|
||||
setThemeState(theme)
|
||||
setAutoMode(!themeFromCookies)
|
||||
}, [cookieKey])
|
||||
}, [preselectedTheme, cookieKey])
|
||||
|
||||
const setTheme = useCallback(
|
||||
(themeToSet: 'auto' | Theme) => {
|
||||
|
||||
@@ -21,6 +21,7 @@ export default buildConfigWithDefaults({
|
||||
importMap: {
|
||||
baseDir: path.resolve(dirname),
|
||||
},
|
||||
theme: 'dark',
|
||||
},
|
||||
cors: ['http://localhost:3000', 'http://localhost:3001'],
|
||||
globals: [MenuGlobal],
|
||||
|
||||
@@ -98,4 +98,11 @@ test.describe('Admin Panel (Root)', () => {
|
||||
await expect(favicons.nth(1)).toHaveAttribute('media', '(prefers-color-scheme: dark)')
|
||||
await expect(favicons.nth(1)).toHaveAttribute('href', /\/payload-favicon-light\.[a-z\d]+\.png/)
|
||||
})
|
||||
|
||||
test('config.admin.theme should restrict the theme', async () => {
|
||||
await page.goto(url.account)
|
||||
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark')
|
||||
await expect(page.locator('#field-theme')).toBeHidden()
|
||||
await expect(page.locator('#field-theme-auto')).toBeHidden()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -138,4 +138,16 @@ describe('Number', () => {
|
||||
'The following field is invalid: withMinRows',
|
||||
)
|
||||
})
|
||||
|
||||
test('should keep data removed on save if deleted', async () => {
|
||||
const input = 1
|
||||
await page.goto(url.create)
|
||||
const field = page.locator('#field-number')
|
||||
await field.fill(String(input))
|
||||
await saveDocAndAssert(page)
|
||||
await expect(field).toHaveValue(String(input))
|
||||
await field.fill('')
|
||||
await saveDocAndAssert(page)
|
||||
await expect(field).toHaveValue('')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user