chore(ui): client-side renders all default field labels, descriptions, and errors

This commit is contained in:
Jacob Fletcher
2024-03-20 15:01:46 -04:00
parent 9f690c1f5d
commit b3d28bac6a
48 changed files with 512 additions and 322 deletions

View File

@@ -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<{
<div className={[baseClass, className].filter(Boolean).join(' ')}>
<h3>{t('general:payloadSettings')}</h3>
<div className={`${baseClass}__language`}>
<Label htmlFor="language-select" label={t('general:language')} />
<FieldLabel htmlFor="language-select" label={t('general:language')} />
<ReactSelect
inputId="language-select"
onChange={async ({ value }) => {

View File

@@ -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 (
<React.Fragment>
<div className={[fieldBaseClass, 'api-key', 'read-only'].filter(Boolean).join(' ')}>
<Label htmlFor={path} label={APIKeyLabel} />
<FieldLabel htmlFor={path} label={APIKeyLabel} />
<input
className={highlightedField ? 'highlight' : undefined}
disabled

View File

@@ -145,7 +145,6 @@ export const DefaultEditView: React.FC = () => {
entitySlug,
user,
collectionSlug,
userSlug,
getVersions,
getDocPermissions,
isEditing,

View File

@@ -97,7 +97,7 @@ export const VersionsViewClient: React.FC<{
)}
{versionCount > 0 && (
<React.Fragment>
<Table columns={columns} data={data?.docs} />
<Table columns={columns} data={data?.docs} fieldMap={componentMap?.fieldMap} />
<div className={`${baseClass}__page-controls`}>
<Pagination
hasNextPage={data.hasNextPage}

View File

@@ -4,6 +4,7 @@ import type { FormFieldBase } from '@payloadcms/ui/fields/shared'
import type { FieldType, Options } from '@payloadcms/ui/forms/useField'
import { TextareaInput } from '@payloadcms/ui/fields/Textarea'
import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
import { useFieldProps } from '@payloadcms/ui/forms/FieldPropsProvider'
import { useAllFormFields } from '@payloadcms/ui/forms/Form'
import { useField } from '@payloadcms/ui/forms/useField'
@@ -26,7 +27,7 @@ type MetaDescriptionProps = FormFieldBase & {
}
export const MetaDescription: React.FC<MetaDescriptionProps> = (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<MetaDescriptionProps> = (props) => {
}}
>
<div className="plugin-seo__field">
{Label}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
{required && (
<span
style={{
@@ -131,7 +131,7 @@ export const MetaDescription: React.FC<MetaDescriptionProps> = (props) => {
}}
>
<TextareaInput
Error={errorMessage} // TODO: Fix
CustomError={errorMessage}
onChange={setValue}
path={pathFromContext}
required={required}

View File

@@ -4,6 +4,7 @@ import type { UploadInputProps } from '@payloadcms/ui/fields/Upload'
import type { FieldType, Options } from '@payloadcms/ui/forms/useField'
import { UploadInput } from '@payloadcms/ui/fields/Upload'
import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
import { useAllFormFields } from '@payloadcms/ui/forms/Form'
import { useField } from '@payloadcms/ui/forms/useField'
import { useConfig } from '@payloadcms/ui/providers/Config'
@@ -22,7 +23,7 @@ type MetaImageProps = UploadInputProps & {
}
export const MetaImage: React.FC<MetaImageProps> = (props) => {
const { Label, hasGenerateImageFn, relationTo, required } = props || {}
const { CustomLabel, hasGenerateImageFn, labelProps, relationTo, required } = props || {}
const field: FieldType<string> = useField(props as Options)
@@ -76,8 +77,7 @@ export const MetaImage: React.FC<MetaImageProps> = (props) => {
}}
>
<div className="plugin-seo__field">
{Label}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
{required && (
<span
style={{
@@ -88,7 +88,6 @@ export const MetaImage: React.FC<MetaImageProps> = (props) => {
*
</span>
)}
{hasGenerateImageFn && (
<React.Fragment>
&nbsp; &mdash; &nbsp;
@@ -127,7 +126,7 @@ export const MetaImage: React.FC<MetaImageProps> = (props) => {
}}
>
<UploadInput
Error={errorMessage} // TODO: Fix
CustomError={errorMessage}
api={api}
collection={collection}
filterOptions={{}}

View File

@@ -5,6 +5,7 @@ import type { Options } from '@payloadcms/ui/forms/useField'
import type { FieldType } from '@payloadcms/ui/forms/useField'
import { TextInput } from '@payloadcms/ui/fields/Text'
import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
import { useFieldProps } from '@payloadcms/ui/forms/FieldPropsProvider'
import { useAllFormFields } from '@payloadcms/ui/forms/Form'
import { useField } from '@payloadcms/ui/forms/useField'
@@ -27,7 +28,7 @@ type MetaTitleProps = FormFieldBase & {
}
export const MetaTitle: React.FC<MetaTitleProps> = (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<MetaTitleProps> = (props) => {
}}
>
<div className="plugin-seo__field">
{Label}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
{required && (
<span
style={{
@@ -88,7 +88,6 @@ export const MetaTitle: React.FC<MetaTitleProps> = (props) => {
*
</span>
)}
{hasGenerateTitleFn && (
<React.Fragment>
&nbsp; &mdash; &nbsp;
@@ -133,7 +132,7 @@ export const MetaTitle: React.FC<MetaTitleProps> = (props) => {
}}
>
<TextInput
Error={errorMessage} // TODO: fix errormessage
CustomError={errorMessage}
onChange={setValue}
path={pathFromContext}
required={required}

View File

@@ -2,6 +2,9 @@
import type { FormFieldBase } from '@payloadcms/ui/fields/shared'
import type { SerializedEditorState } from 'lexical'
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 React, { useCallback } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
@@ -23,11 +26,14 @@ export const RichText: React.FC<
> = (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<
}}
>
<div className={`${baseClass}__wrap`}>
{Error}
{Label}
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
<ErrorBoundary fallbackRender={fallbackRender} onReset={() => {}}>
<LexicalProvider
editorConfig={editorConfig}
@@ -99,7 +105,11 @@ export const RichText: React.FC<
value={value}
/>
</ErrorBoundary>
{Description}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</div>
</div>
)

View File

@@ -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<
}}
>
<div className={`${baseClass}__wrap`}>
{Error}
{Label}
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
<Slate
editor={editor}
key={JSON.stringify({ initialValue, path })} // makes sure slate is completely re-rendered when initialValue changes, bypassing the slate-internal value memoization. That way, external changes to the form will update the editor
@@ -430,7 +436,11 @@ const RichTextField: React.FC<
</div>
</div>
</Slate>
{Description}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</div>
</div>
)

View File

@@ -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 => {
<Fragment>
<span style={{ display: 'inline-block' }}>{prefix}</span>
{prefix ? <Fragment>{' > '}</Fragment> : ''}
<span style={{ display: 'inline-block' }}>{field.fieldComponentProps.Label}</span>
<span style={{ display: 'inline-block' }}>
{'CustomLabel' in field.fieldComponentProps &&
field.fieldComponentProps.CustomLabel !== undefined ? (
field.fieldComponentProps.CustomLabel
) : (
<FieldLabel
{...(('labelProps' in field.fieldComponentProps &&
field.fieldComponentProps.labelProps) ||
{})}
/>
)}
</span>
</Fragment>
)
}
@@ -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<FieldSelectProps> = ({ fieldMap, setSelected
return (
<div className={baseClass}>
<Label label={t('fields:selectFieldsToEdit')} />
<FieldLabel label={t('fields:selectFieldsToEdit')} />
<ReactSelect isMulti onChange={handleChange} options={options} />
</div>
)

View File

@@ -9,7 +9,7 @@ import React, { useCallback, useEffect, useReducer, useState } from 'react'
import type { ListPreferences } from '../TableColumns/index.js'
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<ListDrawerProps> = ({
)}
{moreThanOneAvailableCollection && (
<div className={`${baseClass}__select-collection-wrap`}>
<Label label={t('upload:selectCollectionToBrowse')} />
<FieldLabel label={t('upload:selectCollectionToBrowse')} />
<ReactSelect
className={`${baseClass}__select-collection`}
onChange={setSelectedOption} // this is only changing the options which is not rerunning my effect

View File

@@ -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<UploadProps> = (props) => {
return (
<div className={[fieldBaseClass, baseClass].filter(Boolean).join(' ')}>
<Error message={errorMessage} showError={showError} />
<FieldError message={errorMessage} showError={showError} />
{doc.filename && !replacingFile && (
<FileDetails
canEdit={showCrop || showFocalPoint}

View File

@@ -4,6 +4,9 @@ import type { FieldBase } from 'payload/types'
import type { ArrayField as ArrayFieldType } 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 { FieldMap } from '../../providers/ComponentMap/buildComponentMap/types.js'
@@ -15,7 +18,6 @@ import { DraggableSortableItem } from '../../elements/DraggableSortable/Draggabl
import { DraggableSortable } from '../../elements/DraggableSortable/index.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'
@@ -46,15 +48,17 @@ export type ArrayFieldProps = FormFieldBase & {
export const ArrayField: React.FC<ArrayFieldProps> = (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<ArrayFieldProps> = (props) => {
validate,
} = props
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const { setDocFieldPreferences } = useDocumentInfo()
const { addFieldRow, dispatchFields, setModified } = useForm()
const submitted = useFormSubmitted()
@@ -194,11 +196,17 @@ export const ArrayField: React.FC<ArrayFieldProps> = (props) => {
.join(' ')}
id={`field-${path.replace(/\./g, '__')}`}
>
{showError && <div className={`${baseClass}__error-wrap`}>{Error}</div>}
{showError && (
<div className={`${baseClass}__error-wrap`}>
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
</div>
)}
<header className={`${baseClass}__header`}>
<div className={`${baseClass}__header-wrap`}>
<div className={`${baseClass}__header-content`}>
<h3 className={`${baseClass}__title`}>{Label}</h3>
<h3 className={`${baseClass}__title`}>
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
</h3>
{fieldHasErrors && fieldErrorCount > 0 && (
<ErrorPill count={fieldErrorCount} i18n={i18n} withMessage />
)}
@@ -226,7 +234,11 @@ export const ArrayField: React.FC<ArrayFieldProps> = (props) => {
</ul>
)}
</div>
{Description}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</header>
<NullifyLocaleField fieldValue={value} localized={localized} path={path} />
{(rows.length > 0 || (!valid && (showRequired || showMinRows))) && (

View File

@@ -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<BlocksFieldProps> = (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<BlocksFieldProps> = (props) => {
validate,
} = props
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const { setDocFieldPreferences } = useDocumentInfo()
const { addFieldRow, dispatchFields, setModified } = useForm()
const { code: locale } = useLocale()
@@ -207,11 +210,17 @@ export const BlocksField: React.FC<BlocksFieldProps> = (props) => {
.join(' ')}
id={`field-${path.replace(/\./g, '__')}`}
>
{showError && <div className={`${baseClass}__error-wrap`}>{Error}</div>}
{showError && (
<div className={`${baseClass}__error-wrap`}>
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
</div>
)}
<header className={`${baseClass}__header`}>
<div className={`${baseClass}__header-wrap`}>
<div className={`${baseClass}__heading-with-error`}>
<h3>{Label}</h3>
<h3>
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
</h3>
{fieldHasErrors && fieldErrorCount > 0 && (
<ErrorPill count={fieldErrorCount} i18n={i18n} withMessage />
)}
@@ -239,7 +248,11 @@ export const BlocksField: React.FC<BlocksFieldProps> = (props) => {
</ul>
)}
</div>
{Description}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</header>
<NullifyLocaleField fieldValue={value} localized={localized} path={path} />
{(rows.length > 0 || (!valid && (showRequired || showMinRows))) && (

View File

@@ -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<HTMLInputElement>
labelProps?: LabelProps
name?: string
onToggle: (event: React.ChangeEvent<HTMLInputElement>) => void
partialChecked?: boolean
@@ -26,10 +30,11 @@ export const CheckboxInput: React.FC<Props> = ({
name,
AfterInput,
BeforeInput,
Label,
CustomLabel,
checked,
className,
inputRef,
labelProps,
onToggle,
partialChecked,
readOnly,
@@ -69,7 +74,7 @@ export const CheckboxInput: React.FC<Props> = ({
</span>
{AfterInput}
</div>
{Label}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
</div>
)
}

View File

@@ -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<CheckboxFieldProps> = (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<CheckboxFieldProps> = (props) => {
const { uuid } = useForm()
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const memoizedValidate: ClientValidate = useCallback(
(value, options) => {
if (typeof validate === 'function') {
@@ -88,21 +89,28 @@ const CheckboxField: React.FC<CheckboxFieldProps> = (props) => {
width,
}}
>
<div className={`${baseClass}__error-wrap`}>{Error}</div>
<div className={`${baseClass}__error-wrap`}>
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
</div>
<CheckboxInput
AfterInput={AfterInput}
BeforeInput={BeforeInput}
Label={Label}
CustomLabel={CustomLabel}
checked={checked}
id={fieldID}
inputRef={null}
labelProps={labelProps}
name={path}
onToggle={onToggle}
partialChecked={partialChecked}
readOnly={readOnly}
required={required}
/>
{Description}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</div>
)
}

View File

@@ -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<CodeFieldProps> = (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<CodeFieldProps> = (props) => {
width,
} = props
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const memoizedValidate = useCallback(
(value, options) => {
if (typeof validate === 'function') {
@@ -81,8 +83,8 @@ const CodeField: React.FC<CodeFieldProps> = (props) => {
width,
}}
>
{Error}
{Label}
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
<div>
{BeforeInput}
<CodeEditor
@@ -94,7 +96,7 @@ const CodeField: React.FC<CodeFieldProps> = (props) => {
/>
{AfterInput}
</div>
{Description}
{CustomDescription ? CustomDescription : <FieldDescription {...(descriptionProps || {})} />}
</div>
)
}

View File

@@ -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<CollapsibleFieldProps> = (props) => {
const {
Description,
Label: LabelFromProps,
CustomDescription,
CustomLabel,
className,
descriptionProps,
fieldMap,
initCollapsed = false,
label,
labelProps,
path: pathFromProps,
required,
} = props
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const { path: pathFromContext, readOnly, schemaPath, siblingPermissions } = useFieldProps()
const path = pathFromProps || pathFromContext
@@ -126,7 +126,7 @@ const CollapsibleField: React.FC<CollapsibleFieldProps> = (props) => {
collapsibleStyle={fieldHasErrors ? 'error' : 'default'}
header={
<div className={`${baseClass}__row-label-wrap`}>
{Label}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
{fieldHasErrors && <ErrorPill count={errorCount} i18n={i18n} withMessage />}
</div>
}
@@ -143,7 +143,11 @@ const CollapsibleField: React.FC<CollapsibleFieldProps> = (props) => {
schemaPath={schemaPath}
/>
</CollapsibleElement>
{Description}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</div>
</Fragment>
)

View File

@@ -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<ConfirmPasswordFieldProps> = (props) => {
.filter(Boolean)
.join(' ')}
>
<Error path={path} />
<Label
<FieldError path={path} />
<FieldLabel
htmlFor="field-confirm-password"
label={t('authentication:confirmPassword')}
required

View File

@@ -6,7 +6,7 @@ import { getTranslation } from '@payloadcms/translations'
import React, { useCallback } from 'react'
import { DatePickerField } from '../../elements/DatePicker/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 { useTranslation } from '../../providers/Translation/index.js'
import { fieldBaseClass } from '../shared/index.js'
@@ -16,6 +16,9 @@ const baseClass = 'date-time-field'
import type { DateField, FieldBase } from 'payload/types'
import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription'
import { FieldError } from '@payloadcms/ui/forms/FieldError'
import type { FormFieldBase } from '../shared/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
@@ -34,12 +37,14 @@ const DateTimeField: React.FC<DateFieldProps> = (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<DateFieldProps> = (props) => {
width,
} = props
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const { i18n } = useTranslation()
const memoizedValidate: ClientValidate = useCallback(
@@ -83,8 +86,10 @@ const DateTimeField: React.FC<DateFieldProps> = (props) => {
width,
}}
>
<div className={`${baseClass}__error-wrap`}>{Error}</div>
{Label}
<div className={`${baseClass}__error-wrap`}>
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
</div>
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
<div className={`${baseClass}__input-wrapper`} id={`field-${path.replace(/\./g, '__')}`}>
{BeforeInput}
<DatePickerField
@@ -98,7 +103,11 @@ const DateTimeField: React.FC<DateFieldProps> = (props) => {
/>
{AfterInput}
</div>
{Description}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</div>
)
}

View File

@@ -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<EmailFieldProps> = (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<EmailFieldProps> = (props) => {
width,
} = props
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const { i18n } = useTranslation()
const memoizedValidate: ClientValidate = useCallback(
@@ -71,8 +73,8 @@ const EmailField: React.FC<EmailFieldProps> = (props) => {
width,
}}
>
{Error}
{Label}
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
<div>
{BeforeInput}
<input
@@ -87,7 +89,11 @@ const EmailField: React.FC<EmailFieldProps> = (props) => {
/>
{AfterInput}
</div>
{Description}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</div>
)
}

View File

@@ -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<GroupFieldProps> = (props) => {
const {
Description,
Label: LabelFromProps,
CustomDescription,
CustomLabel,
className,
descriptionProps,
fieldMap,
hideGutter,
label,
required,
labelProps,
style,
width,
} = props
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const { path, permissions, readOnly, schemaPath } = useFieldProps()
const { i18n } = useTranslation()
const isWithinCollapsible = useCollapsible()
@@ -87,10 +86,14 @@ const GroupField: React.FC<GroupFieldProps> = (props) => {
<GroupProvider>
<div className={`${baseClass}__wrap`}>
<div className={`${baseClass}__header`}>
{(Label || Description) && (
{(CustomLabel || CustomDescription) && (
<header>
{Label}
{Description}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</header>
)}
{fieldHasErrors && <ErrorPill count={errorCount} i18n={i18n} withMessage />}

View File

@@ -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

View File

@@ -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<JSONFieldProps> = (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<JSONFieldProps> = (props) => {
width,
} = props
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const [stringValue, setStringValue] = useState<string>()
const [jsonError, setJsonError] = useState<string>()
const [hasLoadedValue, setHasLoadedValue] = useState(false)
@@ -108,8 +111,8 @@ const JSONFieldComponent: React.FC<JSONFieldProps> = (props) => {
width,
}}
>
{Error}
{Label}
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
<div>
{BeforeInput}
<CodeEditor
@@ -121,7 +124,11 @@ const JSONFieldComponent: React.FC<JSONFieldProps> = (props) => {
/>
{AfterInput}
</div>
{Description}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</div>
)
}

View File

@@ -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<NumberFieldProps> = (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<NumberFieldProps> = (props) => {
width,
} = props
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const { i18n, t } = useTranslation()
const memoizedValidate = useCallback(
@@ -149,8 +151,8 @@ const NumberFieldComponent: React.FC<NumberFieldProps> = (props) => {
width,
}}
>
{Error}
{Label}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
{hasMany ? (
<ReactSelect
className={`field-${path.replace(/\./g, '__')}`}
@@ -201,7 +203,11 @@ const NumberFieldComponent: React.FC<NumberFieldProps> = (props) => {
{AfterInput}
</div>
)}
{Description}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</div>
)
}

View File

@@ -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<PasswordFieldProps> = (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<PasswordFieldProps> = (props) => {
width,
} = props
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const memoizedValidate: ClientValidate = useCallback(
(value, options) => {
if (typeof validate === 'function') {
@@ -67,8 +67,8 @@ export const PasswordField: React.FC<PasswordFieldProps> = (props) => {
width,
}}
>
{Error}
{Label}
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
<input
autoComplete={autoComplete}
disabled={formProcessing || disabled}

View File

@@ -5,7 +5,6 @@ import type { ClientValidate, FieldBase } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
import React, { useCallback } from 'react'
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'
@@ -14,6 +13,10 @@ import './index.scss'
const baseClass = 'point'
import { FieldDescription } from '@payloadcms/ui/forms/FieldDescription'
import { FieldError } from '@payloadcms/ui/forms/FieldError'
import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
import type { FormFieldBase } from '../shared/index.js'
export type PointFieldProps = FormFieldBase & {
@@ -30,11 +33,13 @@ const PointField: React.FC<PointFieldProps> = (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<PointFieldProps> = (props) => {
width,
} = props
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const { i18n } = useTranslation()
const memoizedValidate: ClientValidate = useCallback(
@@ -97,10 +100,10 @@ const PointField: React.FC<PointFieldProps> = (props) => {
width,
}}
>
{Error}
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
<ul className={`${baseClass}__wrap`}>
<li>
{Label}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
<div className="input-wrapper">
{BeforeInput}
<input
@@ -117,7 +120,7 @@ const PointField: React.FC<PointFieldProps> = (props) => {
</div>
</li>
<li>
{Label}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
<div className="input-wrapper">
{BeforeInput}
<input
@@ -134,7 +137,11 @@ const PointField: React.FC<PointFieldProps> = (props) => {
</div>
</li>
</ul>
{Description}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</div>
)
}

View File

@@ -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<T = string> = (value: T) => void
const RadioGroupField: React.FC<RadioFieldProps> = (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<RadioFieldProps> = (props) => {
const { uuid } = useForm()
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const memoizedValidate = useCallback(
(value, validationOptions) => {
if (typeof validate === 'function')
@@ -92,8 +95,10 @@ const RadioGroupField: React.FC<RadioFieldProps> = (props) => {
width,
}}
>
<div className={`${baseClass}__error-wrap`}>{Error}</div>
{Label}
<div className={`${baseClass}__error-wrap`}>
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
</div>
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
<ul className={`${baseClass}--group`} id={`field-${path.replace(/\./g, '__')}`}>
{options.map((option) => {
let optionValue = ''
@@ -130,7 +135,11 @@ const RadioGroupField: React.FC<RadioFieldProps> = (props) => {
)
})}
</ul>
{Description}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</div>
)
}

View File

@@ -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<RelationshipFieldProps> = (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<RelationshipFieldProps> = (props) => {
width,
}}
>
{Error}
{Label}
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
{!errorLoading && (
<div className={`${baseClass}__wrap`}>
<ReactSelect
@@ -527,7 +533,11 @@ const RelationshipField: React.FC<RelationshipFieldProps> = (props) => {
</div>
)}
{errorLoading && <div className={`${baseClass}__error-loading`}>{errorLoading}</div>}
{Description}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</div>
)
}

View File

@@ -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<SelectFieldProps> = (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<SelectFieldProps> = (props) => {
width,
} = props
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const { i18n } = useTranslation()
const [options] = useState(formatOptions(optionsFromProps))
@@ -143,8 +145,8 @@ export const SelectField: React.FC<SelectFieldProps> = (props) => {
width,
}}
>
{Error}
{Label}
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
<div>
{BeforeInput}
<ReactSelect
@@ -162,7 +164,11 @@ export const SelectField: React.FC<SelectFieldProps> = (props) => {
/>
{AfterInput}
</div>
{Description}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</div>
)
}

View File

@@ -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<TabsFieldProps> = (props) => {
const {
name,
Description,
CustomDescription,
className,
descriptionProps,
forceRender = false,
indexPath,
path: pathFromProps,
@@ -143,7 +145,11 @@ const TabsField: React.FC<TabsFieldProps> = (props) => {
.filter(Boolean)
.join(' ')}
>
{Description}
{CustomDescription ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
<RenderFields
fieldMap={activeTabConfig.fieldMap}
forceRender={forceRender}

View File

@@ -1,5 +1,8 @@
'use client'
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 from 'react'
import type { TextInputProps } from './types.js'
@@ -13,12 +16,15 @@ export const TextInput: React.FC<TextInputProps> = (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<TextInputProps> = (props) => {
width,
}}
>
{Error}
{Label}
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
{hasMany ? (
<ReactSelect
className={`field-${path.replace(/\./g, '__')}`}
@@ -97,7 +103,11 @@ export const TextInput: React.FC<TextInputProps> = (props) => {
{AfterInput}
</div>
)}
{Description}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</div>
)
}

View File

@@ -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<TextFieldProps> = (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<TextFieldProps> = (props) => {
width,
} = props
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const locale = useLocale()
const { localization: localizationConfig } = useConfig()
@@ -115,12 +114,15 @@ const TextField: React.FC<TextFieldProps> = (props) => {
<TextInput
AfterInput={AfterInput}
BeforeInput={BeforeInput}
Description={Description}
Error={Error}
Label={Label}
CustomDescription={CustomDescription}
CustomError={CustomError}
CustomLabel={CustomLabel}
className={className}
descriptionProps={descriptionProps}
errorProps={errorProps}
hasMany={hasMany}
inputRef={inputRef}
labelProps={labelProps}
maxRows={maxRows}
minRows={minRows}
onChange={

View File

@@ -1,5 +1,8 @@
'use client'
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 from 'react'
import type { TextAreaInputProps } from './types.js'
@@ -12,10 +15,13 @@ export const TextareaInput: React.FC<TextAreaInputProps> = (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<TextAreaInputProps> = (props) => {
width,
}}
>
{Error}
{Label}
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
{BeforeInput}
<label className="textarea-outer" htmlFor={`field-${path.replace(/\./g, '__')}`}>
<div className="textarea-inner">
@@ -66,7 +72,11 @@ export const TextareaInput: React.FC<TextAreaInputProps> = (props) => {
</div>
</label>
{AfterInput}
{Description}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</div>
)
}

View File

@@ -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<TextareaFieldProps> = (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<TextareaFieldProps> = (props) => {
width,
} = props
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const { i18n } = useTranslation()
const { localization } = useConfig()
@@ -72,10 +71,13 @@ const TextareaField: React.FC<TextareaFieldProps> = (props) => {
<TextareaInput
AfterInput={AfterInput}
BeforeInput={BeforeInput}
Description={Description}
Error={Error}
Label={Label}
CustomDescription={CustomDescription}
CustomError={CustomError}
CustomLabel={CustomLabel}
className={className}
descriptionProps={descriptionProps}
errorProps={errorProps}
labelProps={labelProps}
onChange={(e) => {
setValue(e.target.value)
}}

View File

@@ -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<UploadFieldProps, 'filterOptions'> & {
export const UploadInput: React.FC<UploadInputProps> = (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<UploadInputProps> = (props) => {
width,
} = props
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const { i18n, t } = useTranslation()
const [file, setFile] = useState(undefined)
@@ -130,8 +133,8 @@ export const UploadInput: React.FC<UploadInputProps> = (props) => {
width,
}}
>
{Error}
{Label}
{CustomError !== undefined ? CustomError : <FieldError {...(errorProps || {})} />}
{CustomLabel !== undefined ? CustomLabel : <FieldLabel {...(labelProps || {})} />}
{collection?.upload && (
<React.Fragment>
{file && !missingFile && (
@@ -166,7 +169,11 @@ export const UploadInput: React.FC<UploadInputProps> = (props) => {
</div>
</div>
)}
{Description}
{CustomDescription !== undefined ? (
CustomDescription
) : (
<FieldDescription {...(descriptionProps || {})} />
)}
</React.Fragment>
)}
{!readOnly && <DocumentDrawer onSave={onSave} />}

View File

@@ -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<UploadFieldProps> = (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<UploadFieldProps> = (props) => {
width,
} = props
const Label = LabelFromProps || <LabelComp label={label} required={required} />
const {
collections,
routes: { api },
@@ -64,13 +64,16 @@ export const Upload: React.FC<UploadFieldProps> = (props) => {
if (collection.upload) {
return (
<UploadInput
Description={Description}
Error={Error}
Label={Label}
CustomDescription={CustomDescription}
CustomError={CustomError}
CustomLabel={CustomLabel}
api={api}
className={className}
collection={collection}
descriptionProps={descriptionProps}
errorProps={errorProps}
filterOptions={filterOptions}
labelProps={labelProps}
onChange={onChange}
path={path}
readOnly={readOnly}

View File

@@ -1,18 +1,23 @@
import type { User } from 'payload/auth'
import type { Locale, SanitizedLocalizationConfig } from 'payload/config'
import type { DocumentPreferences, Validate } from 'payload/types'
import type { DocumentPreferences, ErrorProps, LabelProps, Validate } from 'payload/types'
import type { FieldDescriptionProps } from '../../forms/FieldDescription/types.js'
export const fieldBaseClass = 'field-type'
export type FormFieldBase = {
AfterInput?: React.ReactNode
BeforeInput?: React.ReactNode
Description?: React.ReactNode
Error?: React.ReactNode
Label?: React.ReactNode
CustomDescription?: React.ReactNode
CustomError?: React.ReactNode
CustomLabel?: React.ReactNode
className?: string
descriptionProps?: FieldDescriptionProps
disabled?: boolean
docPreferences?: DocumentPreferences
errorProps?: ErrorProps
labelProps?: LabelProps
locale?: Locale
localized?: boolean
path?: string

View File

@@ -2,17 +2,17 @@
import { getTranslation } from '@payloadcms/translations'
import React from 'react'
import type { Props } from './types.js'
import type { FieldDescriptionProps } from './types.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { useFieldProps } from '../FieldPropsProvider/index.js'
import './index.scss'
export { Props as FieldDescriptionProps }
export { FieldDescriptionProps }
const baseClass = 'field-description'
export const FieldDescription: React.FC<Props> = (props) => {
export const FieldDescription: React.FC<FieldDescriptionProps> = (props) => {
const { className, description, marginPlacement } = props
const { path } = useFieldProps()

View File

@@ -1,4 +1,4 @@
export type Props = {
export type FieldDescriptionProps = {
className?: string
description?: Record<string, string> | string
marginPlacement?: 'bottom' | 'top'

View File

@@ -11,7 +11,7 @@ import './index.scss'
const baseClass = 'field-error'
export const Error: React.FC<ErrorProps> = (props) => {
export const FieldError: React.FC<ErrorProps> = (props) => {
const {
alignCaret = 'right',
message: messageFromProps,

View File

@@ -10,7 +10,7 @@ import { useFieldProps } from '../FieldPropsProvider/index.js'
import { useForm } from '../Form/context.js'
import './index.scss'
export const Label: React.FC<LabelProps> = (props) => {
export const FieldLabel: React.FC<LabelProps> = (props) => {
const { htmlFor: htmlForFromProps, label: labelFromProps, required = false } = props
const { uuid } = useForm()
const { path } = useFieldProps()

View File

@@ -30,6 +30,7 @@ export const buildComponentMap = (args: {
}
const editViewFromConfig = collectionConfig?.admin?.components?.views?.Edit
const listViewFromConfig = collectionConfig?.admin?.components?.views?.List
const CustomEditView =
@@ -52,6 +53,7 @@ export const buildComponentMap = (args: {
: undefined
const Edit = (CustomEditView as React.FC<EditViewProps>) || DefaultEditView
const List = CustomListView || DefaultListView
const beforeList = collectionConfig?.admin?.components?.BeforeList

View File

@@ -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,11 +34,7 @@ import type {
ReducedBlock,
} from './types.js'
import { RenderCustomComponent } from '../../../elements/RenderCustomComponent/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: {
config: SanitizedConfig
@@ -89,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 &&
@@ -111,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) && (
<Fragment>
{field.admin.components.afterInput.map((Component, i) => (
<Component key={i} />
))}
</Fragment>
)
const AfterInput =
('admin' in field &&
'components' in field.admin &&
'afterInput' in field.admin.components &&
Array.isArray(field.admin?.components?.afterInput) && (
<Fragment>
{field.admin.components.afterInput.map((Component, i) => (
<Component key={i} />
))}
</Fragment>
)) ||
null
const BeforeInput = 'admin' in field &&
field.admin?.components &&
'beforeInput' in field.admin.components &&
Array.isArray(field.admin.components.beforeInput) && (
<Fragment>
{field.admin.components.beforeInput.map((Component, i) => (
<Component key={i} />
))}
</Fragment>
)
const BeforeInput =
('admin' in field &&
field.admin?.components &&
'beforeInput' in field.admin.components &&
Array.isArray(field.admin.components.beforeInput) && (
<Fragment>
{field.admin.components.beforeInput.map((Component, i) => (
<Component key={i} />
))}
</Fragment>
)) ||
null
const Description = (
<RenderCustomComponent
CustomComponent={
field.admin &&
'description' in field.admin &&
field.admin.description &&
typeof field.admin.description === 'function' &&
(field.admin.description as React.FC<any>)
}
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<any>)) ||
undefined
const Error = (
<RenderCustomComponent
CustomComponent={
'admin' in field &&
field.admin.components &&
'Error' in field.admin.components &&
field.admin?.components?.Error
}
DefaultComponent={DefaultError}
componentProps={{ path }}
/>
)
const CustomDescription =
CustomDescriptionComponent !== undefined ? (
<CustomDescriptionComponent {...(descriptionProps || {})} />
) : undefined
const Label = (
<RenderCustomComponent
CustomComponent={
'admin' in field &&
field.admin?.components &&
'Label' in field.admin.components &&
field.admin?.components?.Label
}
DefaultComponent={DefaultLabel}
componentProps={labelProps}
/>
)
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 ? (
<CustomErrorComponent {...(errorProps || {})} />
) : undefined
const CustomLabelComponent =
('admin' in field &&
field.admin?.components &&
'Label' in field.admin.components &&
field.admin?.components?.Label) ||
undefined
const CustomLabel =
CustomLabelComponent !== undefined ? (
<CustomLabelComponent {...(labelProps || {})} />
) : 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,
}
@@ -324,7 +328,7 @@ export const mapFields = (args: {
const collapsibleField: Omit<CollapsibleFieldProps, 'indexPath' | 'permissions'> = {
...baseFieldProps,
Label: CollapsibleLabel,
CustomLabel: CollapsibleLabel,
className: field.admin?.className,
disabled: field.admin?.disabled,
fieldMap: nestedFieldMap,

View File

@@ -1,3 +1,4 @@
import type { HiddenInputFieldProps } from '@payloadcms/ui/fields/HiddenInput'
import type { FieldTypes } from 'payload/config'
import type {
BlockField,
@@ -50,6 +51,7 @@ export type FieldComponentProps =
| DateFieldProps
| EmailFieldProps
| GroupFieldProps
| HiddenInputFieldProps
| JSONFieldProps
| NumberFieldProps
| PointFieldProps