diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 37ba361234..b824c3330b 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -9,10 +9,6 @@ on:
jobs:
changes:
runs-on: ubuntu-latest
- # Cancels any in-progress runs with same ref
- concurrency:
- group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
- cancel-in-progress: true
permissions:
pull-requests: read
outputs:
@@ -268,7 +264,7 @@ jobs:
if: always()
with:
name: test-results
- path: test-results/
+ path: test/test-results/
retention-days: 1
tests-type-generation:
diff --git a/packages/next/src/layouts/Root/index.tsx b/packages/next/src/layouts/Root/index.tsx
index 257bbe70fd..692c39bb92 100644
--- a/packages/next/src/layouts/Root/index.tsx
+++ b/packages/next/src/layouts/Root/index.tsx
@@ -13,7 +13,6 @@ import 'react-toastify/dist/ReactToastify.css'
import { getRequestLanguage } from '../../utilities/getRequestLanguage.js'
import { DefaultEditView } from '../../views/Edit/Default/index.js'
-import { DefaultCell } from '../../views/List/Default/Cell/index.js'
import { DefaultListView } from '../../views/List/Default/index.js'
export const metadata = {
@@ -63,7 +62,6 @@ export const RootLayout = async ({
}
const { componentMap, wrappedChildren } = buildComponentMap({
- DefaultCell,
DefaultEditView,
DefaultListView,
children,
diff --git a/packages/next/src/views/Account/Settings/index.tsx b/packages/next/src/views/Account/Settings/index.tsx
index 2a01c8ff12..aaf9c3657e 100644
--- a/packages/next/src/views/Account/Settings/index.tsx
+++ b/packages/next/src/views/Account/Settings/index.tsx
@@ -1,6 +1,6 @@
'use client'
import { ReactSelect } from '@payloadcms/ui/elements/ReactSelect'
-import { Label } from '@payloadcms/ui/forms/Label'
+import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
import { useTranslation } from '@payloadcms/ui/providers/Translation'
import React from 'react'
@@ -20,7 +20,7 @@ export const Settings: React.FC<{
{t('general:payloadSettings')}
-
+
{
diff --git a/packages/next/src/views/Edit/Default/Auth/APIKey.tsx b/packages/next/src/views/Edit/Default/Auth/APIKey.tsx
index 7363b2b353..a51eeffc88 100644
--- a/packages/next/src/views/Edit/Default/Auth/APIKey.tsx
+++ b/packages/next/src/views/Edit/Default/Auth/APIKey.tsx
@@ -3,8 +3,8 @@ import type { PayloadRequest } from 'payload/types'
import { CopyToClipboard } from '@payloadcms/ui/elements/CopyToClipboard'
import { GenerateConfirmation } from '@payloadcms/ui/elements/GenerateConfirmation'
+import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
import { useFormFields } from '@payloadcms/ui/forms/Form'
-import { Label } from '@payloadcms/ui/forms/Label'
import { useField } from '@payloadcms/ui/forms/useField'
import { useConfig } from '@payloadcms/ui/providers/Config'
import { useTranslation } from '@payloadcms/ui/providers/Translation'
@@ -89,7 +89,7 @@ export const APIKey: React.FC<{ readOnly?: boolean }> = ({ readOnly }) => {
return (
-
+
{
entitySlug,
user,
collectionSlug,
- userSlug,
getVersions,
getDocPermissions,
isEditing,
diff --git a/packages/next/src/views/List/Default/index.tsx b/packages/next/src/views/List/Default/index.tsx
index 8a4603c0a2..51dbad6435 100644
--- a/packages/next/src/views/List/Default/index.tsx
+++ b/packages/next/src/views/List/Default/index.tsx
@@ -16,6 +16,7 @@ import { PublishMany } from '@payloadcms/ui/elements/PublishMany'
import { StaggeredShimmers } from '@payloadcms/ui/elements/ShimmerEffect'
import { useStepNav } from '@payloadcms/ui/elements/StepNav'
import { Table } from '@payloadcms/ui/elements/Table'
+import { RelationshipProvider } from '@payloadcms/ui/elements/Table/RelationshipProvider'
import { UnpublishMany } from '@payloadcms/ui/elements/UnpublishMany'
import { useWindowInfo } from '@payloadcms/ui/elements/WindowInfo'
import { SetViewActions } from '@payloadcms/ui/providers/Actions'
@@ -30,7 +31,6 @@ import LinkImport from 'next/link.js'
import { formatFilesize, isNumber } from 'payload/utilities'
import React, { Fragment, useEffect } from 'react'
-import { RelationshipProvider } from './RelationshipProvider/index.js'
import './index.scss'
const baseClass = 'collection-list'
@@ -134,6 +134,7 @@ export const DefaultListView: React.FC = () => {
uploadConfig: collectionConfig.upload,
}}
data={docs}
+ fieldMap={fieldMap}
/>
)}
diff --git a/packages/next/src/views/Versions/index.client.tsx b/packages/next/src/views/Versions/index.client.tsx
index 1e5b56cf15..ffac980cac 100644
--- a/packages/next/src/views/Versions/index.client.tsx
+++ b/packages/next/src/views/Versions/index.client.tsx
@@ -49,7 +49,7 @@ export const VersionsViewClient: React.FC<{
)}
{versionCount > 0 && (
-
+
code)
+ // make sure the current request locale is the first locale to be handled to skip validation for other locales
+ locales = config.localization.locales.reduce(
+ (acc, { code }) => {
+ if (req.locale === code) return acc
+ acc.push(code)
+ return acc
+ },
+ [req.locale],
+ )
}
let result
@@ -221,7 +229,7 @@ export const duplicateOperation = async = (props) => {
- const { Label, hasGenerateDescriptionFn, path, required } = props
+ const { CustomLabel, hasGenerateDescriptionFn, labelProps, path, required } = props
const { path: pathFromContext } = useFieldProps()
const { t } = useTranslation()
@@ -75,8 +76,7 @@ export const MetaDescription: React.FC = (props) => {
}}
>
- {Label}
-
+ {CustomLabel !== undefined ? CustomLabel :
}
{required && (
= (props) => {
}}
>
= (props) => {
- const { Label, hasGenerateImageFn, relationTo, required } = props || {}
+ const { CustomLabel, hasGenerateImageFn, labelProps, relationTo, required } = props || {}
const field: FieldType = useField(props as Options)
@@ -76,8 +77,7 @@ export const MetaImage: React.FC = (props) => {
}}
>
- {Label}
-
+ {CustomLabel !== undefined ? CustomLabel :
}
{required && (
= (props) => {
*
)}
-
{hasGenerateImageFn && (
—
@@ -127,7 +126,7 @@ export const MetaImage: React.FC = (props) => {
}}
>
= (props) => {
- const { Label, hasGenerateTitleFn, path, required } = props || {}
+ const { CustomLabel, hasGenerateTitleFn, labelProps, path, required } = props || {}
const { path: pathFromContext } = useFieldProps()
const { t } = useTranslation()
@@ -76,8 +77,7 @@ export const MetaTitle: React.FC = (props) => {
}}
>
- {Label}
-
+ {CustomLabel !== undefined ? CustomLabel :
}
{required && (
= (props) => {
*
)}
-
{hasGenerateTitleFn && (
—
@@ -133,7 +132,7 @@ export const MetaTitle: React.FC = (props) => {
}}
>
= (props) => {
const {
name,
- Description,
- Error,
- Label,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
className,
+ descriptionProps,
editorConfig,
+ errorProps,
+ labelProps,
path: pathFromProps,
readOnly,
required,
@@ -75,8 +81,8 @@ export const RichText: React.FC<
}}
>
- {Error}
- {Label}
+ {CustomError !== undefined ? CustomError : }
+ {CustomLabel !== undefined ? CustomLabel : }
{}}>
- {Description}
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
)
diff --git a/packages/richtext-slate/src/field/RichText.tsx b/packages/richtext-slate/src/field/RichText.tsx
index 446bacd3b7..b179bbc879 100644
--- a/packages/richtext-slate/src/field/RichText.tsx
+++ b/packages/richtext-slate/src/field/RichText.tsx
@@ -6,6 +6,9 @@ import type { HistoryEditor } from 'slate-history'
import type { ReactEditor } from 'slate-react'
import { getTranslation } from '@payloadcms/translations'
+import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription'
+import { FieldError } from '@payloadcms/ui/forms/FieldError'
+import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
import { useField } from '@payloadcms/ui/forms/useField'
import { withCondition } from '@payloadcms/ui/forms/withCondition'
import { useEditDepth } from '@payloadcms/ui/providers/EditDepth'
@@ -55,11 +58,14 @@ const RichTextField: React.FC<
> = (props) => {
const {
name,
- Description,
- Error,
- Label,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
className,
+ descriptionProps,
elements,
+ errorProps,
+ labelProps,
leaves,
path: pathFromProps,
placeholder,
@@ -299,8 +305,8 @@ const RichTextField: React.FC<
}}
>
- {Error}
- {Label}
+ {CustomError !== undefined ? CustomError : }
+ {CustomLabel !== undefined ? CustomLabel : }
- {Description}
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
)
diff --git a/packages/ui/src/elements/DuplicateDocument/index.tsx b/packages/ui/src/elements/DuplicateDocument/index.tsx
index 1cd73a6b89..0a1446e645 100644
--- a/packages/ui/src/elements/DuplicateDocument/index.tsx
+++ b/packages/ui/src/elements/DuplicateDocument/index.tsx
@@ -10,6 +10,7 @@ import { toast } from 'react-toastify'
import { useForm, useFormModified } from '../../forms/Form/context.js'
import { useConfig } from '../../providers/Config/index.js'
+import { useLocale } from '../../providers/Locale/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { MinimalTemplate } from '../../templates/Minimal/index.js'
import { requests } from '../../utilities/api.js'
@@ -31,6 +32,7 @@ export const DuplicateDocument: React.FC = ({ id, slug, singularLabel })
const router = useRouter()
const modified = useFormModified()
const { toggleModal } = useModal()
+ const locale = useLocale()
const { setModified } = useForm()
const {
routes: { api },
@@ -53,7 +55,7 @@ export const DuplicateDocument: React.FC = ({ id, slug, singularLabel })
return
}
await requests
- .post(`${serverURL}${api}/${slug}/${id}/duplicate`, {
+ .post(`${serverURL}${api}/${slug}/${id}/duplicate?locale=${locale.code}`, {
body: JSON.stringify({}),
headers: {
'Accept-Language': i18n.language,
@@ -62,7 +64,7 @@ export const DuplicateDocument: React.FC = ({ id, slug, singularLabel })
},
})
.then(async (res) => {
- const { doc, message } = await res.json()
+ const { doc, errors, message } = await res.json()
if (res.status < 400) {
toast.success(
message ||
@@ -72,16 +74,19 @@ export const DuplicateDocument: React.FC = ({ id, slug, singularLabel })
},
)
setModified(false)
- router.push(`${admin}/collections/${slug}/${doc.id}`)
+ router.push(`${admin}/collections/${slug}/${doc.id}?locale=${locale.code}`)
} else {
toast.error(
- message || t('error:unspecific', { label: getTranslation(singularLabel, i18n) }),
+ errors?.[0].message ||
+ message ||
+ t('error:unspecific', { label: getTranslation(singularLabel, i18n) }),
{ autoClose: 5000 },
)
}
})
},
[
+ locale,
modified,
serverURL,
api,
diff --git a/packages/ui/src/elements/FieldSelect/index.tsx b/packages/ui/src/elements/FieldSelect/index.tsx
index f82c450669..0a64b53b31 100644
--- a/packages/ui/src/elements/FieldSelect/index.tsx
+++ b/packages/ui/src/elements/FieldSelect/index.tsx
@@ -4,9 +4,9 @@ import React, { Fragment, useState } from 'react'
import type { FieldMap, MappedField } from '../../providers/ComponentMap/buildComponentMap/types.js'
+import { FieldLabel } from '../../forms/FieldLabel/index.js'
import { useForm } from '../../forms/Form/context.js'
import { createNestedClientFieldPath } from '../../forms/Form/createNestedFieldPath.js'
-import { Label } from '../../forms/Label/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { ReactSelect } from '../ReactSelect/index.js'
import './index.scss'
@@ -23,7 +23,18 @@ const combineLabel = (prefix: JSX.Element, field: MappedField): JSX.Element => {
{prefix}
{prefix ? {' > '} : ''}
- {field.fieldComponentProps.Label}
+
+ {'CustomLabel' in field.fieldComponentProps &&
+ field.fieldComponentProps.CustomLabel !== undefined ? (
+ field.fieldComponentProps.CustomLabel
+ ) : (
+
+ )}
+
)
}
@@ -45,7 +56,7 @@ const reduceFields = (
(field.disableBulkEdit ||
field.unique ||
field.isHidden ||
- field.fieldComponentProps?.readOnly)
+ ('readOnly' in field.fieldComponentProps && field.fieldComponentProps.readOnly))
) {
return fieldsToUse
}
@@ -128,7 +139,7 @@ export const FieldSelect: React.FC = ({ fieldMap, setSelected
return (
-
+
)
diff --git a/packages/ui/src/elements/ListDrawer/DrawerContent.tsx b/packages/ui/src/elements/ListDrawer/DrawerContent.tsx
index 7e74ff73dc..f0b7053d48 100644
--- a/packages/ui/src/elements/ListDrawer/DrawerContent.tsx
+++ b/packages/ui/src/elements/ListDrawer/DrawerContent.tsx
@@ -8,7 +8,7 @@ import React, { useCallback, useEffect, useReducer, useState } from 'react'
import type { ListDrawerProps } from './types.js'
-import { Label } from '../../forms/Label/index.js'
+import { FieldLabel } from '../../forms/FieldLabel/index.js'
import usePayloadAPI from '../../hooks/usePayloadAPI.js'
import { useUseTitleField } from '../../hooks/useUseAsTitle.js'
import { X } from '../../icons/X/index.js'
@@ -264,7 +264,7 @@ export const ListDrawerContent: React.FC = ({
)}
{moreThanOneAvailableCollection && (
-
+
data: unknown[]
+ fieldMap: FieldMap
}
export const Table: React.FC = ({ columns: columnsFromProps, customCellContext, data }) => {
diff --git a/packages/ui/src/elements/TableColumns/buildColumns.tsx b/packages/ui/src/elements/TableColumns/buildColumns.tsx
index e2ed64789e..f3f7f2e3ef 100644
--- a/packages/ui/src/elements/TableColumns/buildColumns.tsx
+++ b/packages/ui/src/elements/TableColumns/buildColumns.tsx
@@ -1,13 +1,16 @@
-import type { CellProps, SanitizedCollectionConfig } from 'payload/types'
-
+import { type CellProps, type SanitizedCollectionConfig } from 'payload/types'
import React from 'react'
-import type { FieldMap } from '../../providers/ComponentMap/buildComponentMap/types.js'
+import type { FieldMap, MappedField } from '../../providers/ComponentMap/buildComponentMap/types.js'
import type { ColumnPreferences } from '../../providers/ListInfo/index.js'
import type { Column } from '../Table/index.js'
import { SelectAll } from '../SelectAll/index.js'
import { SelectRow } from '../SelectRow/index.js'
+import { SortColumn } from '../SortColumn/index.js'
+import { DefaultCell } from '../Table/DefaultCell/index.js'
+
+const fieldIsPresentationalOnly = (field: MappedField): boolean => field.type === 'ui'
export const buildColumns = (args: {
cellProps: Partial[]
@@ -61,6 +64,31 @@ export const buildColumns = (args: {
const name = 'name' in field ? field.name : undefined
+ const Cell =
+ field.CustomCell !== undefined ? (
+ field.CustomCell
+ ) : (
+
+ )
+
+ const Heading = (
+
+ )
+
if (field) {
const column: Column = {
name,
@@ -71,8 +99,8 @@ export const buildColumns = (args: {
link: isFirstActiveColumn,
},
components: {
- Cell: field.Cell,
- Heading: field.Heading,
+ Cell,
+ Heading,
},
label: 'label' in field.fieldComponentProps ? field.fieldComponentProps.label : undefined,
}
diff --git a/packages/ui/src/elements/Upload/index.tsx b/packages/ui/src/elements/Upload/index.tsx
index af000f6e76..da459b30a1 100644
--- a/packages/ui/src/elements/Upload/index.tsx
+++ b/packages/ui/src/elements/Upload/index.tsx
@@ -1,11 +1,11 @@
'use client'
import type { FormState, SanitizedCollectionConfig } from 'payload/types'
+import { FieldError } from '@payloadcms/ui/forms/FieldError'
import { isImage } from 'payload/utilities'
import React, { useCallback, useEffect, useState } from 'react'
import { fieldBaseClass } from '../../fields/shared/index.js'
-import { Error } from '../../forms/Error/index.js'
import { useFormSubmitted } from '../../forms/Form/context.js'
import { useField } from '../../forms/useField/index.js'
import { useDocumentInfo } from '../../providers/DocumentInfo/index.js'
@@ -135,7 +135,7 @@ export const Upload: React.FC = (props) => {
return (
-
+
{doc.filename && !replacingFile && (
= (props) => {
const {
name,
- Description,
- Error,
- Label: LabelFromProps,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
RowLabel,
className,
+ descriptionProps,
+ errorProps,
fieldMap,
forceRender = false,
indexPath,
- label,
+ labelProps,
localized,
maxRows,
minRows,
@@ -65,8 +69,6 @@ export const ArrayField: React.FC = (props) => {
validate,
} = props
- const Label = LabelFromProps ||
-
const { setDocFieldPreferences } = useDocumentInfo()
const { addFieldRow, dispatchFields, setModified } = useForm()
const submitted = useFormSubmitted()
@@ -194,11 +196,17 @@ export const ArrayField: React.FC = (props) => {
.join(' ')}
id={`field-${path.replace(/\./g, '__')}`}
>
- {showError && {Error}
}
+ {showError && (
+
+ {CustomError !== undefined ? CustomError : }
+
+ )}
-
{Label}
+
+ {CustomLabel !== undefined ? CustomLabel : }
+
{fieldHasErrors && fieldErrorCount > 0 && (
)}
@@ -226,7 +234,11 @@ export const ArrayField: React.FC
= (props) => {
)}
- {Description}
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
{(rows.length > 0 || (!valid && (showRequired || showMinRows))) && (
diff --git a/packages/ui/src/fields/Blocks/index.tsx b/packages/ui/src/fields/Blocks/index.tsx
index 331bdb9ede..2cfe6bb750 100644
--- a/packages/ui/src/fields/Blocks/index.tsx
+++ b/packages/ui/src/fields/Blocks/index.tsx
@@ -10,7 +10,6 @@ import { DrawerToggler } from '../../elements/Drawer/index.js'
import { useDrawerSlug } from '../../elements/Drawer/useDrawerSlug.js'
import { ErrorPill } from '../../elements/ErrorPill/index.js'
import { useForm, useFormSubmitted } from '../../forms/Form/context.js'
-import { Label as LabelComp } from '../../forms/Label/index.js'
import { NullifyLocaleField } from '../../forms/NullifyField/index.js'
import { useField } from '../../forms/useField/index.js'
import { useConfig } from '../../providers/Config/index.js'
@@ -28,6 +27,10 @@ const baseClass = 'blocks-field'
import type { FieldPermissions } from 'payload/auth'
import type { BlockField, FieldBase } from 'payload/types'
+import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription'
+import { FieldError } from '@payloadcms/ui/forms/FieldError'
+import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
+
import type {
FieldMap,
ReducedBlock,
@@ -54,14 +57,16 @@ export const BlocksField: React.FC
= (props) => {
const {
name,
- Description,
- Error,
- Label: LabelFromProps,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
blocks,
className,
+ descriptionProps,
+ errorProps,
forceRender = false,
indexPath,
- label,
+ labelProps,
labels: labelsFromProps,
localized,
maxRows,
@@ -72,8 +77,6 @@ export const BlocksField: React.FC = (props) => {
validate,
} = props
- const Label = LabelFromProps ||
-
const { setDocFieldPreferences } = useDocumentInfo()
const { addFieldRow, dispatchFields, setModified } = useForm()
const { code: locale } = useLocale()
@@ -207,11 +210,17 @@ export const BlocksField: React.FC = (props) => {
.join(' ')}
id={`field-${path.replace(/\./g, '__')}`}
>
- {showError && {Error}
}
+ {showError && (
+
+ {CustomError !== undefined ? CustomError : }
+
+ )}
-
{Label}
+
+ {CustomLabel !== undefined ? CustomLabel : }
+
{fieldHasErrors && fieldErrorCount > 0 && (
)}
@@ -239,7 +248,11 @@ export const BlocksField: React.FC = (props) => {
)}
- {Description}
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
{(rows.length > 0 || (!valid && (showRequired || showMinRows))) && (
diff --git a/packages/ui/src/fields/Checkbox/Input.tsx b/packages/ui/src/fields/Checkbox/Input.tsx
index ed79fb4b71..fb90d61433 100644
--- a/packages/ui/src/fields/Checkbox/Input.tsx
+++ b/packages/ui/src/fields/Checkbox/Input.tsx
@@ -1,4 +1,7 @@
'use client'
+import type { LabelProps } from 'packages/payload/src/admin/types.js'
+
+import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
import React from 'react'
import { Check } from '../../icons/Check/index.js'
@@ -7,11 +10,12 @@ import { Line } from '../../icons/Line/index.js'
type Props = {
AfterInput?: React.ReactNode
BeforeInput?: React.ReactNode
- Label?: React.ReactNode
+ CustomLabel?: React.ReactNode
checked?: boolean
className?: string
id?: string
inputRef?: React.RefObject
+ labelProps?: LabelProps
name?: string
onToggle: (event: React.ChangeEvent) => void
partialChecked?: boolean
@@ -26,10 +30,11 @@ export const CheckboxInput: React.FC = ({
name,
AfterInput,
BeforeInput,
- Label,
+ CustomLabel,
checked,
className,
inputRef,
+ labelProps,
onToggle,
partialChecked,
readOnly,
@@ -69,7 +74,7 @@ export const CheckboxInput: React.FC = ({
{AfterInput}
- {Label}
+ {CustomLabel !== undefined ? CustomLabel : }
)
}
diff --git a/packages/ui/src/fields/Checkbox/index.tsx b/packages/ui/src/fields/Checkbox/index.tsx
index 400637924e..3c23188009 100644
--- a/packages/ui/src/fields/Checkbox/index.tsx
+++ b/packages/ui/src/fields/Checkbox/index.tsx
@@ -1,12 +1,13 @@
'use client'
import type { ClientValidate } from 'payload/types'
+import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription'
+import { FieldError } from '@payloadcms/ui/forms/FieldError'
import React, { useCallback } from 'react'
import type { CheckboxFieldProps } from './types.js'
import { useForm } from '../../forms/Form/context.js'
-import { Label as LabelComp } from '../../forms/Label/index.js'
import { useField } from '../../forms/useField/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
import { generateFieldID } from '../../utilities/generateFieldID.js'
@@ -24,13 +25,15 @@ const CheckboxField: React.FC = (props) => {
name,
AfterInput,
BeforeInput,
- Description,
- Error,
- Label: LabelFromProps,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
checked: checkedFromProps,
className,
+ descriptionProps,
disableFormData,
- label,
+ errorProps,
+ labelProps,
onChange: onChangeFromProps,
partialChecked,
path: pathFromProps,
@@ -43,8 +46,6 @@ const CheckboxField: React.FC = (props) => {
const { uuid } = useForm()
- const Label = LabelFromProps ||
-
const memoizedValidate: ClientValidate = useCallback(
(value, options) => {
if (typeof validate === 'function') {
@@ -88,21 +89,28 @@ const CheckboxField: React.FC = (props) => {
width,
}}
>
- {Error}
+
+ {CustomError !== undefined ? CustomError : }
+
- {Description}
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
)
}
diff --git a/packages/ui/src/fields/Code/index.tsx b/packages/ui/src/fields/Code/index.tsx
index 384ba3bfea..44026aefd9 100644
--- a/packages/ui/src/fields/Code/index.tsx
+++ b/packages/ui/src/fields/Code/index.tsx
@@ -2,12 +2,14 @@
'use client'
import type { CodeField as CodeFieldType, FieldBase } from 'payload/types'
+import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription'
+import { FieldError } from '@payloadcms/ui/forms/FieldError'
+import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
import React, { useCallback } from 'react'
import type { FormFieldBase } from '../shared/index.js'
import { CodeEditor } from '../../elements/CodeEditor/index.js'
-import { Label as LabelComp } from '../../forms/Label/index.js'
import { useField } from '../../forms/useField/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
import { fieldBaseClass } from '../shared/index.js'
@@ -34,12 +36,14 @@ const CodeField: React.FC = (props) => {
name,
AfterInput,
BeforeInput,
- Description,
- Error,
- Label: LabelFromProps,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
className,
+ descriptionProps,
editorOptions = {},
- label,
+ errorProps,
+ labelProps,
language = 'javascript',
path: pathFromProps,
readOnly,
@@ -49,8 +53,6 @@ const CodeField: React.FC = (props) => {
width,
} = props
- const Label = LabelFromProps ||
-
const memoizedValidate = useCallback(
(value, options) => {
if (typeof validate === 'function') {
@@ -81,8 +83,8 @@ const CodeField: React.FC = (props) => {
width,
}}
>
- {Error}
- {Label}
+ {CustomError !== undefined ? CustomError : }
+ {CustomLabel !== undefined ? CustomLabel : }
{BeforeInput}
= (props) => {
/>
{AfterInput}
- {Description}
+ {CustomDescription ? CustomDescription : }
)
}
diff --git a/packages/ui/src/fields/Collapsible/index.tsx b/packages/ui/src/fields/Collapsible/index.tsx
index ba88b92732..c6c71c1406 100644
--- a/packages/ui/src/fields/Collapsible/index.tsx
+++ b/packages/ui/src/fields/Collapsible/index.tsx
@@ -7,7 +7,6 @@ import React, { Fragment, useCallback, useEffect, useState } from 'react'
import { Collapsible as CollapsibleElement } from '../../elements/Collapsible/index.js'
import { ErrorPill } from '../../elements/ErrorPill/index.js'
import { useFieldProps } from '../../forms/FieldPropsProvider/index.js'
-import { Label as LabelComp } from '../../forms/Label/index.js'
import { RenderFields } from '../../forms/RenderFields/index.js'
import { WatchChildErrors } from '../../forms/WatchChildErrors/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
@@ -22,6 +21,9 @@ const baseClass = 'collapsible-field'
import type { FieldPermissions } from 'payload/auth'
import type { FieldBase } from 'payload/types'
+import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription'
+import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
+
import type { FieldMap } from '../../providers/ComponentMap/buildComponentMap/types.js'
import type { FormFieldBase } from '../shared/index.js'
@@ -36,18 +38,16 @@ export type CollapsibleFieldProps = FormFieldBase & {
const CollapsibleField: React.FC = (props) => {
const {
- Description,
- Label: LabelFromProps,
+ CustomDescription,
+ CustomLabel,
className,
+ descriptionProps,
fieldMap,
initCollapsed = false,
- label,
+ labelProps,
path: pathFromProps,
- required,
} = props
- const Label = LabelFromProps ||
-
const { path: pathFromContext, readOnly, schemaPath, siblingPermissions } = useFieldProps()
const path = pathFromProps || pathFromContext
@@ -126,7 +126,7 @@ const CollapsibleField: React.FC = (props) => {
collapsibleStyle={fieldHasErrors ? 'error' : 'default'}
header={
- {Label}
+ {CustomLabel !== undefined ? CustomLabel : }
{fieldHasErrors && }
}
@@ -143,7 +143,11 @@ const CollapsibleField: React.FC = (props) => {
schemaPath={schemaPath}
/>
- {Description}
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
)
diff --git a/packages/ui/src/fields/ConfirmPassword/index.tsx b/packages/ui/src/fields/ConfirmPassword/index.tsx
index f2a753a588..4eebd8069e 100644
--- a/packages/ui/src/fields/ConfirmPassword/index.tsx
+++ b/packages/ui/src/fields/ConfirmPassword/index.tsx
@@ -3,9 +3,9 @@ import type { FormField } from 'payload/types'
import React, { useCallback } from 'react'
-import { Error } from '../../forms/Error/index.js'
+import { FieldError } from '../../forms/FieldError/index.js'
+import { FieldLabel } from '../../forms/FieldLabel/index.js'
import { useFormFields } from '../../forms/Form/context.js'
-import { Label } from '../../forms/Label/index.js'
import { useField } from '../../forms/useField/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { fieldBaseClass } from '../shared/index.js'
@@ -50,8 +50,8 @@ export const ConfirmPassword: React.FC = (props) => {
.filter(Boolean)
.join(' ')}
>
-
-
+ = (props) => {
name,
AfterInput,
BeforeInput,
- Description,
- Error,
- Label: LabelFromProps,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
className,
date: datePickerProps,
- label,
+ descriptionProps,
+ errorProps,
+ labelProps,
path: pathFromProps,
placeholder,
readOnly,
@@ -49,8 +54,6 @@ const DateTimeField: React.FC = (props) => {
width,
} = props
- const Label = LabelFromProps ||
-
const { i18n } = useTranslation()
const memoizedValidate: ClientValidate = useCallback(
@@ -83,8 +86,10 @@ const DateTimeField: React.FC = (props) => {
width,
}}
>
- {Error}
- {Label}
+
+ {CustomError !== undefined ? CustomError : }
+
+ {CustomLabel !== undefined ? CustomLabel : }
{BeforeInput}
= (props) => {
/>
{AfterInput}
- {Description}
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
)
}
diff --git a/packages/ui/src/fields/Email/index.tsx b/packages/ui/src/fields/Email/index.tsx
index a557551bf1..039221c8d2 100644
--- a/packages/ui/src/fields/Email/index.tsx
+++ b/packages/ui/src/fields/Email/index.tsx
@@ -3,11 +3,13 @@ import type { ClientValidate } from 'payload/types'
import type { EmailField as EmailFieldType, FieldBase } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
+import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription'
+import { FieldError } from '@payloadcms/ui/forms/FieldError'
+import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
import React, { useCallback } from 'react'
import type { FormFieldBase } from '../shared/index.js'
-import { Label as LabelComp } from '../../forms/Label/index.js'
import { useField } from '../../forms/useField/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
@@ -28,12 +30,14 @@ const EmailField: React.FC = (props) => {
name,
AfterInput,
BeforeInput,
- Description,
- Error,
- Label: LabelFromProps,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
autoComplete,
className,
- label,
+ descriptionProps,
+ errorProps,
+ labelProps,
path: pathFromProps,
placeholder,
readOnly,
@@ -43,8 +47,6 @@ const EmailField: React.FC = (props) => {
width,
} = props
- const Label = LabelFromProps ||
-
const { i18n } = useTranslation()
const memoizedValidate: ClientValidate = useCallback(
@@ -71,8 +73,8 @@ const EmailField: React.FC = (props) => {
width,
}}
>
- {Error}
- {Label}
+ {CustomError !== undefined ? CustomError : }
+ {CustomLabel !== undefined ? CustomLabel : }
{BeforeInput}
= (props) => {
/>
{AfterInput}
- {Description}
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
)
}
diff --git a/packages/ui/src/fields/Group/index.tsx b/packages/ui/src/fields/Group/index.tsx
index a3be9a066c..da9309b7cf 100644
--- a/packages/ui/src/fields/Group/index.tsx
+++ b/packages/ui/src/fields/Group/index.tsx
@@ -2,6 +2,8 @@
import type { FieldPermissions } from 'payload/auth'
import type { FieldBase } from 'payload/types'
+import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription'
+import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
import React, { Fragment } from 'react'
import type { FieldMap } from '../../providers/ComponentMap/buildComponentMap/types.js'
@@ -10,7 +12,6 @@ import type { FormFieldBase } from '../shared/index.js'
import { useCollapsible } from '../../elements/Collapsible/provider.js'
import { ErrorPill } from '../../elements/ErrorPill/index.js'
import { useFieldProps } from '../../forms/FieldPropsProvider/index.js'
-import { Label as LabelComp } from '../../forms/Label/index.js'
import { RenderFields } from '../../forms/RenderFields/index.js'
import { WatchChildErrors } from '../../forms/WatchChildErrors/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
@@ -36,19 +37,17 @@ export type GroupFieldProps = FormFieldBase & {
const GroupField: React.FC
= (props) => {
const {
- Description,
- Label: LabelFromProps,
+ CustomDescription,
+ CustomLabel,
className,
+ descriptionProps,
fieldMap,
hideGutter,
- label,
- required,
+ labelProps,
style,
width,
} = props
- const Label = LabelFromProps ||
-
const { path, permissions, readOnly, schemaPath } = useFieldProps()
const { i18n } = useTranslation()
const isWithinCollapsible = useCollapsible()
@@ -87,10 +86,14 @@ const GroupField: React.FC = (props) => {
- {(Label || Description) && (
+ {(CustomLabel || CustomDescription) && (
- {Label}
- {Description}
+ {CustomLabel !== undefined ? CustomLabel : }
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
)}
{fieldHasErrors &&
}
diff --git a/packages/ui/src/fields/HiddenInput/index.tsx b/packages/ui/src/fields/HiddenInput/index.tsx
index d8632d3b5f..cf0b38a8f4 100644
--- a/packages/ui/src/fields/HiddenInput/index.tsx
+++ b/packages/ui/src/fields/HiddenInput/index.tsx
@@ -1,10 +1,12 @@
'use client'
import React, { useEffect } from 'react'
+import type { FormFieldBase } from '../index.js'
+
import { useField } from '../../forms/useField/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
-export type HiddenInputFieldProps = {
+export type HiddenInputFieldProps = FormFieldBase & {
disableModifyingForm?: false
name: string
path?: string
diff --git a/packages/ui/src/fields/JSON/index.tsx b/packages/ui/src/fields/JSON/index.tsx
index 57ca4e4942..ee5600dcd1 100644
--- a/packages/ui/src/fields/JSON/index.tsx
+++ b/packages/ui/src/fields/JSON/index.tsx
@@ -4,7 +4,7 @@ import type { ClientValidate } from 'payload/types'
import React, { useCallback, useEffect, useState } from 'react'
import { CodeEditor } from '../../elements/CodeEditor/index.js'
-import { Label as LabelComp } from '../../forms/Label/index.js'
+import { FieldLabel } from '../../forms/FieldLabel/index.js'
import { useField } from '../../forms/useField/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
import { fieldBaseClass } from '../shared/index.js'
@@ -14,6 +14,9 @@ const baseClass = 'json-field'
import type { FieldBase, JSONField as JSONFieldType } from 'payload/types'
+import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription'
+import { FieldError } from '@payloadcms/ui/forms/FieldError'
+
import type { FormFieldBase } from '../shared/index.js'
export type JSONFieldProps = FormFieldBase & {
@@ -29,12 +32,14 @@ const JSONFieldComponent: React.FC
= (props) => {
name,
AfterInput,
BeforeInput,
- Description,
- Error,
- Label: LabelFromProps,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
className,
+ descriptionProps,
editorOptions,
- label,
+ errorProps,
+ labelProps,
path: pathFromProps,
readOnly,
required,
@@ -43,8 +48,6 @@ const JSONFieldComponent: React.FC = (props) => {
width,
} = props
- const Label = LabelFromProps ||
-
const [stringValue, setStringValue] = useState()
const [jsonError, setJsonError] = useState()
const [hasLoadedValue, setHasLoadedValue] = useState(false)
@@ -108,8 +111,8 @@ const JSONFieldComponent: React.FC = (props) => {
width,
}}
>
- {Error}
- {Label}
+ {CustomError !== undefined ? CustomError : }
+ {CustomLabel !== undefined ? CustomLabel : }
{BeforeInput}
= (props) => {
/>
{AfterInput}
- {Description}
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
)
}
diff --git a/packages/ui/src/fields/Number/index.tsx b/packages/ui/src/fields/Number/index.tsx
index c0cc8edf4a..59200c1042 100644
--- a/packages/ui/src/fields/Number/index.tsx
+++ b/packages/ui/src/fields/Number/index.tsx
@@ -3,6 +3,9 @@
import type { FieldBase, NumberField as NumberFieldType } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
+import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription'
+import { FieldError } from '@payloadcms/ui/forms/FieldError'
+import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
import { isNumber } from 'payload/utilities'
import React, { useCallback, useEffect, useState } from 'react'
@@ -10,7 +13,6 @@ import type { Option } from '../../elements/ReactSelect/types.js'
import type { FormFieldBase } from '../shared/index.js'
import { ReactSelect } from '../../elements/ReactSelect/index.js'
-import { Label as LabelComp } from '../../forms/Label/index.js'
import { useField } from '../../forms/useField/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
@@ -36,12 +38,14 @@ const NumberFieldComponent: React.FC
= (props) => {
name,
AfterInput,
BeforeInput,
- Description,
- Error,
- Label: LabelFromProps,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
className,
+ descriptionProps,
+ errorProps,
hasMany = false,
- label,
+ labelProps,
max = Infinity,
maxRows = Infinity,
min = -Infinity,
@@ -56,8 +60,6 @@ const NumberFieldComponent: React.FC = (props) => {
width,
} = props
- const Label = LabelFromProps ||
-
const { i18n, t } = useTranslation()
const memoizedValidate = useCallback(
@@ -149,8 +151,8 @@ const NumberFieldComponent: React.FC = (props) => {
width,
}}
>
- {Error}
- {Label}
+ {CustomLabel !== undefined ? CustomLabel : }
+ {CustomError !== undefined ? CustomError : }
{hasMany ? (
= (props) => {
{AfterInput}
)}
- {Description}
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
)
}
diff --git a/packages/ui/src/fields/Password/index.tsx b/packages/ui/src/fields/Password/index.tsx
index 2cc215acb8..06eaae230d 100644
--- a/packages/ui/src/fields/Password/index.tsx
+++ b/packages/ui/src/fields/Password/index.tsx
@@ -1,11 +1,12 @@
'use client'
import type { ClientValidate, Description, Validate } from 'payload/types'
+import { FieldError } from '@payloadcms/ui/forms/FieldError'
+import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
import React, { useCallback } from 'react'
import type { FormFieldBase } from '../shared/index.js'
-import { Label as LabelComp } from '../../forms/Label/index.js'
import { useField } from '../../forms/useField/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
import { fieldBaseClass } from '../shared/index.js'
@@ -28,12 +29,13 @@ export type PasswordFieldProps = FormFieldBase & {
export const PasswordField: React.FC = (props) => {
const {
name,
- Error,
- Label: LabelFromProps,
+ CustomError,
+ CustomLabel,
autoComplete,
className,
disabled,
- label,
+ errorProps,
+ labelProps,
path: pathFromProps,
required,
style,
@@ -41,8 +43,6 @@ export const PasswordField: React.FC = (props) => {
width,
} = props
- const Label = LabelFromProps ||
-
const memoizedValidate: ClientValidate = useCallback(
(value, options) => {
if (typeof validate === 'function') {
@@ -67,8 +67,8 @@ export const PasswordField: React.FC = (props) => {
width,
}}
>
- {Error}
- {Label}
+ {CustomError !== undefined ? CustomError : }
+ {CustomLabel !== undefined ? CustomLabel : }
= (props) => {
name,
AfterInput,
BeforeInput,
- Description,
- Error,
- Label: LabelFromProps,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
className,
- label,
+ descriptionProps,
+ errorProps,
+ labelProps,
path: pathFromProps,
placeholder,
readOnly,
@@ -45,8 +50,6 @@ const PointField: React.FC = (props) => {
width,
} = props
- const Label = LabelFromProps ||
-
const { i18n } = useTranslation()
const memoizedValidate: ClientValidate = useCallback(
@@ -97,10 +100,10 @@ const PointField: React.FC = (props) => {
width,
}}
>
- {Error}
+ {CustomError !== undefined ? CustomError : }
- {Description}
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
)
}
diff --git a/packages/ui/src/fields/RadioGroup/index.tsx b/packages/ui/src/fields/RadioGroup/index.tsx
index 54d4f6f08f..6560742fe2 100644
--- a/packages/ui/src/fields/RadioGroup/index.tsx
+++ b/packages/ui/src/fields/RadioGroup/index.tsx
@@ -6,8 +6,8 @@ import type { FieldBase, Option } from 'payload/types'
import { optionIsObject } from 'payload/types'
import React, { useCallback } from 'react'
+import { FieldLabel } from '../../forms/FieldLabel/index.js'
import { useForm } from '../../forms/Form/context.js'
-import { Label as LabelComp } from '../../forms/Label/index.js'
import { useField } from '../../forms/useField/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
import { fieldBaseClass } from '../shared/index.js'
@@ -16,6 +16,9 @@ import './index.scss'
const baseClass = 'radio-group'
+import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription'
+import { FieldError } from '@payloadcms/ui/forms/FieldError'
+
import type { FormFieldBase } from '../shared/index.js'
export type RadioFieldProps = FormFieldBase & {
@@ -34,11 +37,13 @@ export type OnChange = (value: T) => void
const RadioGroupField: React.FC = (props) => {
const {
name,
- Description,
- Error,
- Label: LabelFromProps,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
className,
- label,
+ descriptionProps,
+ errorProps,
+ labelProps,
layout = 'horizontal',
onChange: onChangeFromProps,
options = [],
@@ -53,8 +58,6 @@ const RadioGroupField: React.FC = (props) => {
const { uuid } = useForm()
- const Label = LabelFromProps ||
-
const memoizedValidate = useCallback(
(value, validationOptions) => {
if (typeof validate === 'function')
@@ -92,8 +95,10 @@ const RadioGroupField: React.FC = (props) => {
width,
}}
>
- {Error}
- {Label}
+
+ {CustomError !== undefined ? CustomError : }
+
+ {CustomLabel !== undefined ? CustomLabel : }
{options.map((option) => {
let optionValue = ''
@@ -130,7 +135,11 @@ const RadioGroupField: React.FC = (props) => {
)
})}
- {Description}
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
)
}
diff --git a/packages/ui/src/fields/Relationship/index.tsx b/packages/ui/src/fields/Relationship/index.tsx
index 2d47e217d8..6ec6e59920 100644
--- a/packages/ui/src/fields/Relationship/index.tsx
+++ b/packages/ui/src/fields/Relationship/index.tsx
@@ -2,6 +2,9 @@
import type { PaginatedDocs } from 'payload/database'
import type { Where } from 'payload/types'
+import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription'
+import { FieldError } from '@payloadcms/ui/forms/FieldError'
+import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
import { wordBoundariesRegex } from 'payload/utilities'
import qs from 'qs'
import React, { useCallback, useEffect, useReducer, useRef, useState } from 'react'
@@ -36,13 +39,16 @@ export { RelationshipFieldProps }
const RelationshipField: React.FC = (props) => {
const {
name,
- Description,
- Error,
- Label,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
allowCreate = true,
className,
+ descriptionProps,
+ errorProps,
hasMany,
isSortable = true,
+ labelProps,
path: pathFromProps,
readOnly,
relationTo,
@@ -434,8 +440,8 @@ const RelationshipField: React.FC = (props) => {
width,
}}
>
- {Error}
- {Label}
+ {CustomError !== undefined ? CustomError : }
+ {CustomLabel !== undefined ? CustomLabel : }
{!errorLoading && (
= (props) => {
)}
{errorLoading && {errorLoading}
}
- {Description}
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
)
}
diff --git a/packages/ui/src/fields/Select/index.tsx b/packages/ui/src/fields/Select/index.tsx
index 4349e97cf6..eb93fa4705 100644
--- a/packages/ui/src/fields/Select/index.tsx
+++ b/packages/ui/src/fields/Select/index.tsx
@@ -3,12 +3,14 @@
import type { ClientValidate, FieldBase, Option, OptionObject } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
+import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription'
+import { FieldError } from '@payloadcms/ui/forms/FieldError'
+import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
import React, { useCallback, useState } from 'react'
import type { FormFieldBase } from '../shared/index.js'
import { ReactSelect } from '../../elements/ReactSelect/index.js'
-import { Label as LabelComp } from '../../forms/Label/index.js'
import { useField } from '../../forms/useField/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
@@ -45,14 +47,16 @@ export const SelectField: React.FC = (props) => {
name,
AfterInput,
BeforeInput,
- Description,
- Error,
- Label: LabelFromProps,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
className,
+ descriptionProps,
+ errorProps,
hasMany = false,
isClearable = true,
isSortable = true,
- label,
+ labelProps,
onChange: onChangeFromProps,
options: optionsFromProps = [],
path: pathFromProps,
@@ -63,8 +67,6 @@ export const SelectField: React.FC = (props) => {
width,
} = props
- const Label = LabelFromProps ||
-
const { i18n } = useTranslation()
const [options] = useState(formatOptions(optionsFromProps))
@@ -143,8 +145,8 @@ export const SelectField: React.FC = (props) => {
width,
}}
>
- {Error}
- {Label}
+ {CustomError !== undefined ? CustomError : }
+ {CustomLabel !== undefined ? CustomLabel : }
{BeforeInput}
= (props) => {
/>
{AfterInput}
- {Description}
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
)
}
diff --git a/packages/ui/src/fields/Tabs/index.tsx b/packages/ui/src/fields/Tabs/index.tsx
index 64333a6666..b8fb8559e4 100644
--- a/packages/ui/src/fields/Tabs/index.tsx
+++ b/packages/ui/src/fields/Tabs/index.tsx
@@ -3,6 +3,7 @@ import type { FieldPermissions } from 'payload/auth'
import type { DocumentPreferences } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
+import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription'
import { toKebabCase } from 'payload/utilities'
import React, { useCallback, useEffect, useState } from 'react'
@@ -39,8 +40,9 @@ export type TabsFieldProps = FormFieldBase & {
const TabsField: React.FC = (props) => {
const {
name,
- Description,
+ CustomDescription,
className,
+ descriptionProps,
forceRender = false,
indexPath,
path: pathFromProps,
@@ -143,7 +145,11 @@ const TabsField: React.FC = (props) => {
.filter(Boolean)
.join(' ')}
>
- {Description}
+ {CustomDescription ? (
+ CustomDescription
+ ) : (
+
+ )}
= (props) => {
const {
AfterInput,
BeforeInput,
- Description,
- Error,
- Label,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
className,
+ descriptionProps,
+ errorProps,
hasMany,
inputRef,
+ labelProps,
maxRows,
onChange,
onKeyDown,
@@ -52,8 +58,8 @@ export const TextInput: React.FC = (props) => {
width,
}}
>
- {Error}
- {Label}
+ {CustomError !== undefined ? CustomError : }
+ {CustomLabel !== undefined ? CustomLabel : }
{hasMany ? (
= (props) => {
{AfterInput}
)}
- {Description}
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
)
}
diff --git a/packages/ui/src/fields/Text/index.tsx b/packages/ui/src/fields/Text/index.tsx
index e03a90cd9c..1b30c9b233 100644
--- a/packages/ui/src/fields/Text/index.tsx
+++ b/packages/ui/src/fields/Text/index.tsx
@@ -7,7 +7,6 @@ import React, { useCallback, useEffect, useState } from 'react'
import type { Option } from '../../elements/ReactSelect/types.js'
import type { TextFieldProps, TextInputProps } from './types.js'
-import { Label as LabelComp } from '../../forms/Label/index.js'
import { useField } from '../../forms/useField/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
import { useConfig } from '../../providers/Config/index.js'
@@ -23,13 +22,15 @@ const TextField: React.FC = (props) => {
name,
AfterInput,
BeforeInput,
- Description,
- Error,
- Label: LabelFromProps,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
className,
+ descriptionProps,
+ errorProps,
hasMany,
inputRef,
- label,
+ labelProps,
localized,
maxLength,
maxRows,
@@ -44,8 +45,6 @@ const TextField: React.FC = (props) => {
width,
} = props
- const Label = LabelFromProps ||
-
const locale = useLocale()
const { localization: localizationConfig } = useConfig()
@@ -115,12 +114,15 @@ const TextField: React.FC = (props) => {
= (props) => {
const {
AfterInput,
BeforeInput,
- Description,
- Error,
- Label,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
className,
+ descriptionProps,
+ errorProps,
+ labelProps,
onChange,
path,
placeholder,
@@ -46,8 +52,8 @@ export const TextareaInput: React.FC = (props) => {
width,
}}
>
- {Error}
- {Label}
+ {CustomError !== undefined ? CustomError : }
+ {CustomLabel !== undefined ? CustomLabel : }
{BeforeInput}
{AfterInput}
- {Description}
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
)
}
diff --git a/packages/ui/src/fields/Textarea/index.tsx b/packages/ui/src/fields/Textarea/index.tsx
index 6f97db97d4..86b257c846 100644
--- a/packages/ui/src/fields/Textarea/index.tsx
+++ b/packages/ui/src/fields/Textarea/index.tsx
@@ -7,7 +7,6 @@ import React, { useCallback } from 'react'
import type { TextAreaInputProps, TextareaFieldProps } from './types.js'
-import { Label as LabelComp } from '../../forms/Label/index.js'
import { useField } from '../../forms/useField/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
import { useConfig } from '../../providers/Config/index.js'
@@ -23,11 +22,13 @@ const TextareaField: React.FC = (props) => {
name,
AfterInput,
BeforeInput,
- Description,
- Error,
- Label: LabelFromProps,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
className,
- label,
+ descriptionProps,
+ errorProps,
+ labelProps,
locale,
localized,
maxLength,
@@ -42,8 +43,6 @@ const TextareaField: React.FC = (props) => {
width,
} = props
- const Label = LabelFromProps ||
-
const { i18n } = useTranslation()
const { localization } = useConfig()
@@ -72,10 +71,13 @@ const TextareaField: React.FC = (props) => {
{
setValue(e.target.value)
}}
diff --git a/packages/ui/src/fields/Upload/Input.tsx b/packages/ui/src/fields/Upload/Input.tsx
index 8ecd67c1f3..d59e9c4230 100644
--- a/packages/ui/src/fields/Upload/Input.tsx
+++ b/packages/ui/src/fields/Upload/Input.tsx
@@ -3,6 +3,9 @@
import type { ClientCollectionConfig, FilterOptionsResult, UploadField } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
+import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription'
+import { FieldError } from '@payloadcms/ui/forms/FieldError'
+import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
import React, { useCallback, useEffect, useState } from 'react'
import type { DocumentDrawerProps } from '../../elements/DocumentDrawer/types.js'
@@ -13,7 +16,6 @@ import { Button } from '../../elements/Button/index.js'
import { useDocumentDrawer } from '../../elements/DocumentDrawer/index.js'
import { FileDetails } from '../../elements/FileDetails/index.js'
import { useListDrawer } from '../../elements/ListDrawer/index.js'
-import { Label as LabelComp } from '../../forms/Label/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { fieldBaseClass } from '../shared/index.js'
import './index.scss'
@@ -33,14 +35,17 @@ export type UploadInputProps = Omit & {
export const UploadInput: React.FC = (props) => {
const {
- Description,
- Error,
- Label: LabelFromProps,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
api = '/api',
className,
collection,
+ descriptionProps,
+ errorProps,
filterOptions,
label,
+ labelProps,
onChange,
readOnly,
relationTo,
@@ -52,8 +57,6 @@ export const UploadInput: React.FC = (props) => {
width,
} = props
- const Label = LabelFromProps ||
-
const { i18n, t } = useTranslation()
const [file, setFile] = useState(undefined)
@@ -130,8 +133,8 @@ export const UploadInput: React.FC = (props) => {
width,
}}
>
- {Error}
- {Label}
+ {CustomError !== undefined ? CustomError : }
+ {CustomLabel !== undefined ? CustomLabel : }
{collection?.upload && (
{file && !missingFile && (
@@ -166,7 +169,11 @@ export const UploadInput: React.FC = (props) => {
)}
- {Description}
+ {CustomDescription !== undefined ? (
+ CustomDescription
+ ) : (
+
+ )}
)}
{!readOnly && }
diff --git a/packages/ui/src/fields/Upload/index.tsx b/packages/ui/src/fields/Upload/index.tsx
index 4e5f1c3605..08e5e69ed3 100644
--- a/packages/ui/src/fields/Upload/index.tsx
+++ b/packages/ui/src/fields/Upload/index.tsx
@@ -4,7 +4,6 @@ import React, { useCallback } from 'react'
import type { UploadInputProps } from './Input.js'
import type { UploadFieldProps } from './types.js'
-import { Label as LabelComp } from '../../forms/Label/index.js'
import { useField } from '../../forms/useField/index.js'
import { useConfig } from '../../providers/Config/index.js'
import { UploadInput } from './Input.js'
@@ -15,11 +14,14 @@ export type { UploadInputProps }
export const Upload: React.FC = (props) => {
const {
- Description,
- Error,
- Label: LabelFromProps,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
className,
+ descriptionProps,
+ errorProps,
label,
+ labelProps,
path: pathFromProps,
readOnly,
relationTo,
@@ -29,8 +31,6 @@ export const Upload: React.FC = (props) => {
width,
} = props
- const Label = LabelFromProps ||
-
const {
collections,
routes: { api },
@@ -64,13 +64,16 @@ export const Upload: React.FC = (props) => {
if (collection.upload) {
return (
= (props) => {
+export const FieldDescription: React.FC = (props) => {
const { className, description, marginPlacement } = props
const { path } = useFieldProps()
diff --git a/packages/ui/src/forms/FieldDescription/types.ts b/packages/ui/src/forms/FieldDescription/types.ts
index 9b96dee989..2e9a25b516 100644
--- a/packages/ui/src/forms/FieldDescription/types.ts
+++ b/packages/ui/src/forms/FieldDescription/types.ts
@@ -1,4 +1,4 @@
-export type Props = {
+export type FieldDescriptionProps = {
className?: string
description?: Record | string
marginPlacement?: 'bottom' | 'top'
diff --git a/packages/ui/src/forms/Error/index.scss b/packages/ui/src/forms/FieldError/index.scss
similarity index 100%
rename from packages/ui/src/forms/Error/index.scss
rename to packages/ui/src/forms/FieldError/index.scss
diff --git a/packages/ui/src/forms/Error/index.tsx b/packages/ui/src/forms/FieldError/index.tsx
similarity index 94%
rename from packages/ui/src/forms/Error/index.tsx
rename to packages/ui/src/forms/FieldError/index.tsx
index 50af442784..176223600a 100644
--- a/packages/ui/src/forms/Error/index.tsx
+++ b/packages/ui/src/forms/FieldError/index.tsx
@@ -11,7 +11,7 @@ import './index.scss'
const baseClass = 'field-error'
-export const Error: React.FC = (props) => {
+export const FieldError: React.FC = (props) => {
const {
alignCaret = 'right',
message: messageFromProps,
diff --git a/packages/ui/src/forms/Label/index.scss b/packages/ui/src/forms/FieldLabel/index.scss
similarity index 100%
rename from packages/ui/src/forms/Label/index.scss
rename to packages/ui/src/forms/FieldLabel/index.scss
diff --git a/packages/ui/src/forms/Label/index.tsx b/packages/ui/src/forms/FieldLabel/index.tsx
similarity index 93%
rename from packages/ui/src/forms/Label/index.tsx
rename to packages/ui/src/forms/FieldLabel/index.tsx
index b0afc118cb..9ae6a2caf5 100644
--- a/packages/ui/src/forms/Label/index.tsx
+++ b/packages/ui/src/forms/FieldLabel/index.tsx
@@ -10,7 +10,7 @@ import { useFieldProps } from '../FieldPropsProvider/index.js'
import { useForm } from '../Form/context.js'
import './index.scss'
-export const Label: React.FC = (props) => {
+export const FieldLabel: React.FC = (props) => {
const { htmlFor: htmlForFromProps, label: labelFromProps, required = false } = props
const { uuid } = useForm()
const { path } = useFieldProps()
diff --git a/packages/ui/src/providers/ComponentMap/buildComponentMap/index.tsx b/packages/ui/src/providers/ComponentMap/buildComponentMap/index.tsx
index 5fb7a087cc..ca6fa52946 100644
--- a/packages/ui/src/providers/ComponentMap/buildComponentMap/index.tsx
+++ b/packages/ui/src/providers/ComponentMap/buildComponentMap/index.tsx
@@ -8,7 +8,6 @@ import { mapActions } from './mapActions.js'
import { mapFields } from './mapFields.js'
export const buildComponentMap = (args: {
- DefaultCell: React.FC
DefaultEditView: React.FC
DefaultListView: React.FC
children: React.ReactNode
@@ -18,20 +17,20 @@ export const buildComponentMap = (args: {
componentMap: ComponentMap
wrappedChildren: React.ReactNode
} => {
- const {
- DefaultCell,
- DefaultEditView,
- DefaultListView,
- children,
- config,
- readOnly: readOnlyOverride,
- } = args
+ const { DefaultEditView, DefaultListView, children, config, readOnly: readOnlyOverride } = args
// Collections
const collections = config.collections.reduce((acc, collectionConfig) => {
const { slug, fields } = collectionConfig
+ const internalCollections = ['payload-preferences', 'payload-migrations']
+
+ if (internalCollections.includes(slug)) {
+ return acc
+ }
+
const editViewFromConfig = collectionConfig?.admin?.components?.views?.Edit
+
const listViewFromConfig = collectionConfig?.admin?.components?.views?.List
const CustomEditView =
@@ -54,6 +53,7 @@ export const buildComponentMap = (args: {
: undefined
const Edit = (CustomEditView as React.FC) || DefaultEditView
+
const List = CustomListView || DefaultListView
const beforeList = collectionConfig?.admin?.components?.BeforeList
@@ -103,7 +103,6 @@ export const buildComponentMap = (args: {
collectionConfig,
}),
fieldMap: mapFields({
- DefaultCell,
config,
fieldSchema: fields,
readOnly: readOnlyOverride,
@@ -141,7 +140,6 @@ export const buildComponentMap = (args: {
globalConfig,
}),
fieldMap: mapFields({
- DefaultCell,
config,
fieldSchema: fields,
readOnly: readOnlyOverride,
diff --git a/packages/ui/src/providers/ComponentMap/buildComponentMap/mapFields.tsx b/packages/ui/src/providers/ComponentMap/buildComponentMap/mapFields.tsx
index b194be7dd4..0451a3299e 100644
--- a/packages/ui/src/providers/ComponentMap/buildComponentMap/mapFields.tsx
+++ b/packages/ui/src/providers/ComponentMap/buildComponentMap/mapFields.tsx
@@ -1,3 +1,4 @@
+import type { FieldDescriptionProps } from '@payloadcms/ui/forms/FieldDescription'
import type { CellProps, Field, FieldWithPath, LabelProps, SanitizedConfig } from 'payload/types'
import { fieldAffectsData, fieldIsPresentationalOnly } from 'payload/types'
@@ -25,7 +26,6 @@ import type { TextFieldProps } from '../../../fields/Text/types.js'
import type { TextareaFieldProps } from '../../../fields/Textarea/types.js'
import type { UploadFieldProps } from '../../../fields/Upload/types.js'
import type { FormFieldBase } from '../../../fields/shared/index.js'
-import type { Props as FieldDescription } from '../../../forms/FieldDescription/types.js'
import type {
FieldComponentProps,
FieldMap,
@@ -34,15 +34,9 @@ import type {
ReducedBlock,
} from './types.js'
-import { RenderCustomComponent } from '../../../elements/RenderCustomComponent/index.js'
-import { SortColumn } from '../../../elements/SortColumn/index.js'
import { HiddenInput } from '../../../fields/HiddenInput/index.js'
-import { Error as DefaultError } from '../../../forms/Error/index.js'
-import { FieldDescription as DefaultDescription } from '../../../forms/FieldDescription/index.js'
-import { Label as DefaultLabel } from '../../../forms/Label/index.js'
export const mapFields = (args: {
- DefaultCell?: React.FC
config: SanitizedConfig
/**
* If mapFields is used outside of collections, you might not want it to add an id field
@@ -54,7 +48,6 @@ export const mapFields = (args: {
readOnly?: boolean
}): FieldMap => {
const {
- DefaultCell,
config,
disableAddingID,
fieldSchema,
@@ -92,7 +85,7 @@ export const mapFields = (args: {
required: 'required' in field ? field.required : undefined,
}
- const descriptionProps: FieldDescription = {
+ const descriptionProps: FieldDescriptionProps = {
description:
field.admin &&
'description' in field.admin &&
@@ -107,7 +100,6 @@ export const mapFields = (args: {
field.fields &&
Array.isArray(field.fields) &&
mapFields({
- DefaultCell,
config,
fieldSchema: field.fields,
filter,
@@ -115,75 +107,83 @@ export const mapFields = (args: {
readOnly: readOnlyOverride,
})
- const AfterInput = 'admin' in field &&
- 'components' in field.admin &&
- 'afterInput' in field.admin.components &&
- Array.isArray(field.admin?.components?.afterInput) && (
-
- {field.admin.components.afterInput.map((Component, i) => (
-
- ))}
-
- )
+ const AfterInput =
+ ('admin' in field &&
+ 'components' in field.admin &&
+ 'afterInput' in field.admin.components &&
+ Array.isArray(field.admin?.components?.afterInput) && (
+
+ {field.admin.components.afterInput.map((Component, i) => (
+
+ ))}
+
+ )) ||
+ null
- const BeforeInput = 'admin' in field &&
- field.admin?.components &&
- 'beforeInput' in field.admin.components &&
- Array.isArray(field.admin.components.beforeInput) && (
-
- {field.admin.components.beforeInput.map((Component, i) => (
-
- ))}
-
- )
+ const BeforeInput =
+ ('admin' in field &&
+ field.admin?.components &&
+ 'beforeInput' in field.admin.components &&
+ Array.isArray(field.admin.components.beforeInput) && (
+
+ {field.admin.components.beforeInput.map((Component, i) => (
+
+ ))}
+
+ )) ||
+ null
- const Description = (
- )
- }
- DefaultComponent={DefaultDescription}
- componentProps={descriptionProps}
- />
- )
+ const CustomDescriptionComponent =
+ (field.admin &&
+ 'description' in field.admin &&
+ field.admin.description &&
+ typeof field.admin.description === 'function' &&
+ (field.admin.description as React.FC)) ||
+ undefined
- const Error = (
-
- )
+ const CustomDescription =
+ CustomDescriptionComponent !== undefined ? (
+
+ ) : undefined
- const Label = (
-
- )
+ const CustomErrorComponent =
+ ('admin' in field &&
+ field.admin?.components &&
+ 'Error' in field.admin.components &&
+ field.admin?.components?.Error) ||
+ undefined
+
+ const errorProps = {
+ path,
+ }
+
+ const CustomError =
+ CustomErrorComponent !== undefined ? (
+
+ ) : undefined
+
+ const CustomLabelComponent =
+ ('admin' in field &&
+ field.admin?.components &&
+ 'Label' in field.admin.components &&
+ field.admin?.components?.Label) ||
+ undefined
+
+ const CustomLabel =
+ CustomLabelComponent !== undefined ? (
+
+ ) : undefined
const baseFieldProps: FormFieldBase = {
AfterInput,
BeforeInput,
- Description,
- Error,
- Label,
+ CustomDescription,
+ CustomError,
+ CustomLabel,
+ descriptionProps,
disabled: 'admin' in field && 'disabled' in field.admin ? field.admin?.disabled : false,
+ errorProps,
+ labelProps,
path,
required: 'required' in field ? field.required : undefined,
}
@@ -240,7 +240,6 @@ export const mapFields = (args: {
case 'blocks': {
const blocks = field.blocks.map((block) => {
const blockFieldMap = mapFields({
- DefaultCell,
config,
fieldSchema: block.fields,
filter,
@@ -329,7 +328,7 @@ export const mapFields = (args: {
const collapsibleField: Omit = {
...baseFieldProps,
- Label: CollapsibleLabel,
+ CustomLabel: CollapsibleLabel,
className: field.admin?.className,
disabled: field.admin?.disabled,
fieldMap: nestedFieldMap,
@@ -530,7 +529,6 @@ export const mapFields = (args: {
// `tabs` fields require a field map of each of its tab's nested fields
const tabs = field.tabs.map((tab) => {
const tabFieldMap = mapFields({
- DefaultCell,
config,
fieldSchema: tab.fields,
filter,
@@ -645,40 +643,16 @@ export const mapFields = (args: {
}
}
- const Cell = (
-
- )
-
- const Heading = (
-
- )
-
const reducedField: MappedField = {
name: 'name' in field ? field.name : undefined,
type: field.type,
- Cell,
+ CustomCell: CustomCellComponent ? (
+
+ ) : undefined,
CustomField: CustomFieldComponent ? (
) : undefined,
- Heading,
+ cellComponentProps,
disableBulkEdit:
'admin' in field && 'disableBulkEdit' in field.admin && field.admin.disableBulkEdit,
fieldComponentProps,
@@ -706,8 +680,9 @@ export const mapFields = (args: {
result.push({
name: 'id',
type: 'text',
- Cell: DefaultCell ? : null,
- Heading: ,
+ cellComponentProps: {
+ name: 'id',
+ },
fieldComponentProps: {
name: 'id',
label: 'ID',
diff --git a/packages/ui/src/providers/ComponentMap/buildComponentMap/types.ts b/packages/ui/src/providers/ComponentMap/buildComponentMap/types.ts
index 31f7259f4c..d6600bc040 100644
--- a/packages/ui/src/providers/ComponentMap/buildComponentMap/types.ts
+++ b/packages/ui/src/providers/ComponentMap/buildComponentMap/types.ts
@@ -1,6 +1,8 @@
+import type { HiddenInputFieldProps } from '@payloadcms/ui/fields/HiddenInput'
import type { FieldTypes } from 'payload/config'
import type {
BlockField,
+ CellProps,
SanitizedCollectionConfig,
SanitizedGlobalConfig,
TabsField,
@@ -49,6 +51,7 @@ export type FieldComponentProps =
| DateFieldProps
| EmailFieldProps
| GroupFieldProps
+ | HiddenInputFieldProps
| JSONFieldProps
| NumberFieldProps
| PointFieldProps
@@ -62,9 +65,9 @@ export type FieldComponentProps =
| UploadFieldProps
export type MappedField = {
- Cell: React.ReactNode
+ CustomCell?: React.ReactNode
CustomField?: React.ReactNode
- Heading: React.ReactNode
+ cellComponentProps: CellProps
disableBulkEdit?: boolean
disabled?: boolean
fieldComponentProps: FieldComponentProps