fix: email and username fields rendering in drawers (#7520)
Fixes https://github.com/payloadcms/payload/issues/7428 Now email and username fields are rendered with the RenderFields component, making them behave similarly to other fields. They now appear and can respect doc permissions, readOnly settings, etc.
This commit is contained in:
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@@ -56,6 +56,13 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "node-terminal"
|
"type": "node-terminal"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "node --no-deprecation test/dev.js login-with-username",
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"name": "Run Dev Login-With-Username",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "node-terminal"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "pnpm run dev plugin-cloud-storage",
|
"command": "pnpm run dev plugin-cloud-storage",
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
|
|||||||
@@ -1,45 +1,103 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import type { LoginWithUsernameOptions } from 'payload'
|
import type { FieldPermissions, LoginWithUsernameOptions } from 'payload'
|
||||||
|
|
||||||
import { EmailField, TextField, useTranslation } from '@payloadcms/ui'
|
import { EmailField, RenderFields, TextField, useTranslation } from '@payloadcms/ui'
|
||||||
import { email, username } from 'payload/shared'
|
import { email, username } from 'payload/shared'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
loginWithUsername?: LoginWithUsernameOptions | false
|
loginWithUsername?: LoginWithUsernameOptions | false
|
||||||
}
|
}
|
||||||
export const EmailAndUsernameFields: React.FC<Props> = ({ loginWithUsername }) => {
|
function EmailFieldComponent(props: Props) {
|
||||||
|
const { loginWithUsername } = props
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const requireEmail = !loginWithUsername || (loginWithUsername && loginWithUsername.requireEmail)
|
const requireEmail = !loginWithUsername || (loginWithUsername && loginWithUsername.requireEmail)
|
||||||
const requireUsername = loginWithUsername && loginWithUsername.requireUsername
|
|
||||||
const showEmailField =
|
const showEmailField =
|
||||||
!loginWithUsername || loginWithUsername?.requireEmail || loginWithUsername?.allowEmailLogin
|
!loginWithUsername || loginWithUsername?.requireEmail || loginWithUsername?.allowEmailLogin
|
||||||
|
|
||||||
|
if (showEmailField) {
|
||||||
|
return (
|
||||||
|
<EmailField
|
||||||
|
autoComplete="off"
|
||||||
|
label={t('general:email')}
|
||||||
|
name="email"
|
||||||
|
path="email"
|
||||||
|
required={requireEmail}
|
||||||
|
validate={email}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function UsernameFieldComponent(props: Props) {
|
||||||
|
const { loginWithUsername } = props
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const requireUsername = loginWithUsername && loginWithUsername.requireUsername
|
||||||
const showUsernameField = Boolean(loginWithUsername)
|
const showUsernameField = Boolean(loginWithUsername)
|
||||||
|
|
||||||
return (
|
if (showUsernameField) {
|
||||||
<React.Fragment>
|
return (
|
||||||
{showEmailField && (
|
<TextField
|
||||||
<EmailField
|
label={t('authentication:username')}
|
||||||
autoComplete="email"
|
name="username"
|
||||||
label={t('general:email')}
|
path="username"
|
||||||
name="email"
|
required={requireUsername}
|
||||||
path="email"
|
validate={username}
|
||||||
required={requireEmail}
|
/>
|
||||||
validate={email}
|
)
|
||||||
/>
|
}
|
||||||
)}
|
|
||||||
|
|
||||||
{showUsernameField && (
|
return null
|
||||||
<TextField
|
}
|
||||||
label={t('authentication:username')}
|
|
||||||
name="username"
|
type RenderEmailAndUsernameFieldsProps = {
|
||||||
path="username"
|
className?: string
|
||||||
required={requireUsername}
|
loginWithUsername?: LoginWithUsernameOptions | false
|
||||||
validate={username}
|
operation?: 'create' | 'update'
|
||||||
/>
|
permissions?: {
|
||||||
)}
|
[fieldName: string]: FieldPermissions
|
||||||
</React.Fragment>
|
}
|
||||||
|
readOnly: boolean
|
||||||
|
}
|
||||||
|
export function RenderEmailAndUsernameFields(props: RenderEmailAndUsernameFieldsProps) {
|
||||||
|
const { className, loginWithUsername, operation, permissions, readOnly } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RenderFields
|
||||||
|
className={className}
|
||||||
|
fieldMap={[
|
||||||
|
{
|
||||||
|
name: 'email',
|
||||||
|
type: 'text',
|
||||||
|
CustomField: <EmailFieldComponent loginWithUsername={loginWithUsername} />,
|
||||||
|
cellComponentProps: null,
|
||||||
|
fieldComponentProps: { type: 'email', readOnly },
|
||||||
|
fieldIsPresentational: false,
|
||||||
|
isFieldAffectingData: true,
|
||||||
|
localized: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'username',
|
||||||
|
type: 'text',
|
||||||
|
CustomField: <UsernameFieldComponent loginWithUsername={loginWithUsername} />,
|
||||||
|
cellComponentProps: null,
|
||||||
|
fieldComponentProps: { type: 'text', readOnly },
|
||||||
|
fieldIsPresentational: false,
|
||||||
|
isFieldAffectingData: true,
|
||||||
|
localized: false,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
forceRender
|
||||||
|
operation={operation}
|
||||||
|
path=""
|
||||||
|
permissions={permissions}
|
||||||
|
readOnly={readOnly}
|
||||||
|
schemaPath=""
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
import { getFormState } from '@payloadcms/ui/shared'
|
import { getFormState } from '@payloadcms/ui/shared'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { EmailAndUsernameFields } from '../../elements/EmailAndUsername/index.js'
|
import { RenderEmailAndUsernameFields } from '../../elements/EmailAndUsername/index.js'
|
||||||
|
|
||||||
export const CreateFirstUserClient: React.FC<{
|
export const CreateFirstUserClient: React.FC<{
|
||||||
initialState: FormState
|
initialState: FormState
|
||||||
@@ -57,7 +57,12 @@ export const CreateFirstUserClient: React.FC<{
|
|||||||
redirect={admin}
|
redirect={admin}
|
||||||
validationOperation="create"
|
validationOperation="create"
|
||||||
>
|
>
|
||||||
<EmailAndUsernameFields loginWithUsername={loginWithUsername} />
|
<RenderEmailAndUsernameFields
|
||||||
|
className="emailAndUsername"
|
||||||
|
loginWithUsername={loginWithUsername}
|
||||||
|
operation="create"
|
||||||
|
readOnly={false}
|
||||||
|
/>
|
||||||
<PasswordField
|
<PasswordField
|
||||||
label={t('authentication:newPassword')}
|
label={t('authentication:newPassword')}
|
||||||
name="password"
|
name="password"
|
||||||
|
|||||||
@@ -3,3 +3,7 @@
|
|||||||
margin-bottom: var(--base);
|
margin-bottom: var(--base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.emailAndUsername {
|
||||||
|
margin-bottom: var(--base);
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { toast } from 'sonner'
|
|||||||
|
|
||||||
import type { Props } from './types.js'
|
import type { Props } from './types.js'
|
||||||
|
|
||||||
import { EmailAndUsernameFields } from '../../../../elements/EmailAndUsername/index.js'
|
import { RenderEmailAndUsernameFields } from '../../../../elements/EmailAndUsername/index.js'
|
||||||
import { APIKey } from './APIKey.js'
|
import { APIKey } from './APIKey.js'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ export const Auth: React.FC<Props> = (props) => {
|
|||||||
const dispatchFields = useFormFields((reducer) => reducer[1])
|
const dispatchFields = useFormFields((reducer) => reducer[1])
|
||||||
const modified = useFormModified()
|
const modified = useFormModified()
|
||||||
const { i18n, t } = useTranslation()
|
const { i18n, t } = useTranslation()
|
||||||
const { isInitializing } = useDocumentInfo()
|
const { docPermissions, isInitializing } = useDocumentInfo()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
routes: { api },
|
routes: { api },
|
||||||
@@ -138,7 +138,12 @@ export const Auth: React.FC<Props> = (props) => {
|
|||||||
<div className={[baseClass, className].filter(Boolean).join(' ')}>
|
<div className={[baseClass, className].filter(Boolean).join(' ')}>
|
||||||
{!disableLocalStrategy && (
|
{!disableLocalStrategy && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<EmailAndUsernameFields loginWithUsername={loginWithUsername} />
|
<RenderEmailAndUsernameFields
|
||||||
|
loginWithUsername={loginWithUsername}
|
||||||
|
operation={operation}
|
||||||
|
permissions={docPermissions?.fields}
|
||||||
|
readOnly={readOnly}
|
||||||
|
/>
|
||||||
{(showPasswordFields || requirePassword) && (
|
{(showPasswordFields || requirePassword) && (
|
||||||
<div className={`${baseClass}__changing-password`}>
|
<div className={`${baseClass}__changing-password`}>
|
||||||
<PasswordField
|
<PasswordField
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export type ArrayFieldProps = {
|
|||||||
name?: string
|
name?: string
|
||||||
validate?: ArrayFieldValidation
|
validate?: ArrayFieldValidation
|
||||||
width?: string
|
width?: string
|
||||||
} & FormFieldBase
|
} & Omit<FormFieldBase, 'validate'>
|
||||||
|
|
||||||
export type ArrayFieldLabelComponent = LabelComponent<'array'>
|
export type ArrayFieldLabelComponent = LabelComponent<'array'>
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export type BlocksFieldProps = {
|
|||||||
slug?: string
|
slug?: string
|
||||||
validate?: BlockFieldValidation
|
validate?: BlockFieldValidation
|
||||||
width?: string
|
width?: string
|
||||||
} & FormFieldBase
|
} & Omit<FormFieldBase, 'validate'>
|
||||||
|
|
||||||
export type ReducedBlock = {
|
export type ReducedBlock = {
|
||||||
LabelComponent: Block['admin']['components']['Label']
|
LabelComponent: Block['admin']['components']['Label']
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export type CheckboxFieldProps = {
|
|||||||
path?: string
|
path?: string
|
||||||
validate?: CheckboxFieldValidation
|
validate?: CheckboxFieldValidation
|
||||||
width?: string
|
width?: string
|
||||||
} & FormFieldBase
|
} & Omit<FormFieldBase, 'validate'>
|
||||||
|
|
||||||
export type CheckboxFieldLabelComponent = LabelComponent<'checkbox'>
|
export type CheckboxFieldLabelComponent = LabelComponent<'checkbox'>
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export type CodeFieldProps = {
|
|||||||
path?: string
|
path?: string
|
||||||
validate?: CodeFieldValidation
|
validate?: CodeFieldValidation
|
||||||
width: string
|
width: string
|
||||||
} & FormFieldBase
|
} & Omit<FormFieldBase, 'validate'>
|
||||||
|
|
||||||
export type CodeFieldLabelComponent = LabelComponent<'code'>
|
export type CodeFieldLabelComponent = LabelComponent<'code'>
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export type DateFieldProps = {
|
|||||||
placeholder?: DateField['admin']['placeholder'] | string
|
placeholder?: DateField['admin']['placeholder'] | string
|
||||||
validate?: DateFieldValidation
|
validate?: DateFieldValidation
|
||||||
width?: string
|
width?: string
|
||||||
} & FormFieldBase
|
} & Omit<FormFieldBase, 'validate'>
|
||||||
|
|
||||||
export type DateFieldLabelComponent = LabelComponent<'date'>
|
export type DateFieldLabelComponent = LabelComponent<'date'>
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export type EmailFieldProps = {
|
|||||||
placeholder?: EmailField['admin']['placeholder']
|
placeholder?: EmailField['admin']['placeholder']
|
||||||
validate?: EmailFieldValidation
|
validate?: EmailFieldValidation
|
||||||
width?: string
|
width?: string
|
||||||
} & FormFieldBase
|
} & Omit<FormFieldBase, 'validate'>
|
||||||
|
|
||||||
export type EmailFieldLabelComponent = LabelComponent<'email'>
|
export type EmailFieldLabelComponent = LabelComponent<'email'>
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export type JSONFieldProps = {
|
|||||||
path?: string
|
path?: string
|
||||||
validate?: JSONFieldValidation
|
validate?: JSONFieldValidation
|
||||||
width?: string
|
width?: string
|
||||||
} & FormFieldBase
|
} & Omit<FormFieldBase, 'validate'>
|
||||||
|
|
||||||
export type JSONFieldLabelComponent = LabelComponent<'json'>
|
export type JSONFieldLabelComponent = LabelComponent<'json'>
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export type NumberFieldProps = {
|
|||||||
step?: number
|
step?: number
|
||||||
validate?: NumberFieldValidation
|
validate?: NumberFieldValidation
|
||||||
width?: string
|
width?: string
|
||||||
} & FormFieldBase
|
} & Omit<FormFieldBase, 'validate'>
|
||||||
|
|
||||||
export type NumberFieldLabelComponent = LabelComponent<'number'>
|
export type NumberFieldLabelComponent = LabelComponent<'number'>
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export type PointFieldProps = {
|
|||||||
step?: number
|
step?: number
|
||||||
validate?: PointFieldValidation
|
validate?: PointFieldValidation
|
||||||
width?: string
|
width?: string
|
||||||
} & FormFieldBase
|
} & Omit<FormFieldBase, 'validate'>
|
||||||
|
|
||||||
export type PointFieldLabelComponent = LabelComponent<'point'>
|
export type PointFieldLabelComponent = LabelComponent<'point'>
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export type RadioFieldProps = {
|
|||||||
validate?: RadioFieldValidation
|
validate?: RadioFieldValidation
|
||||||
value?: string
|
value?: string
|
||||||
width?: string
|
width?: string
|
||||||
} & FormFieldBase
|
} & Omit<FormFieldBase, 'validate'>
|
||||||
|
|
||||||
export type OnChange<T = string> = (value: T) => void
|
export type OnChange<T = string> = (value: T) => void
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export type RelationshipFieldProps = {
|
|||||||
sortOptions?: RelationshipField['admin']['sortOptions']
|
sortOptions?: RelationshipField['admin']['sortOptions']
|
||||||
validate?: RelationshipFieldValidation
|
validate?: RelationshipFieldValidation
|
||||||
width?: string
|
width?: string
|
||||||
} & FormFieldBase
|
} & Omit<FormFieldBase, 'validate'>
|
||||||
|
|
||||||
export type RelationshipFieldLabelComponent = LabelComponent<'relationship'>
|
export type RelationshipFieldLabelComponent = LabelComponent<'relationship'>
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export type RichTextComponentProps = {
|
|||||||
richTextComponentMap?: Map<string, MappedField[] | React.ReactNode>
|
richTextComponentMap?: Map<string, MappedField[] | React.ReactNode>
|
||||||
validate?: RichTextFieldValidation
|
validate?: RichTextFieldValidation
|
||||||
width?: string
|
width?: string
|
||||||
} & FormFieldBase
|
} & Omit<FormFieldBase, 'validate'>
|
||||||
|
|
||||||
export type RichTextFieldLabelComponent = LabelComponent<'richText'>
|
export type RichTextFieldLabelComponent = LabelComponent<'richText'>
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export type SelectFieldProps = {
|
|||||||
validate?: SelectFieldValidation
|
validate?: SelectFieldValidation
|
||||||
value?: string
|
value?: string
|
||||||
width?: string
|
width?: string
|
||||||
} & FormFieldBase
|
} & Omit<FormFieldBase, 'validate'>
|
||||||
|
|
||||||
export type SelectFieldLabelComponent = LabelComponent<'select'>
|
export type SelectFieldLabelComponent = LabelComponent<'select'>
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export type TextFieldProps = {
|
|||||||
placeholder?: TextField['admin']['placeholder']
|
placeholder?: TextField['admin']['placeholder']
|
||||||
validate?: TextFieldValidation
|
validate?: TextFieldValidation
|
||||||
width?: string
|
width?: string
|
||||||
} & FormFieldBase
|
} & Omit<FormFieldBase, 'validate'>
|
||||||
|
|
||||||
export type TextFieldLabelComponent = LabelComponent<'text'>
|
export type TextFieldLabelComponent = LabelComponent<'text'>
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export type TextareaFieldProps = {
|
|||||||
rows?: number
|
rows?: number
|
||||||
validate?: TextareaFieldValidation
|
validate?: TextareaFieldValidation
|
||||||
width?: string
|
width?: string
|
||||||
} & FormFieldBase
|
} & Omit<FormFieldBase, 'validate'>
|
||||||
|
|
||||||
export type TextareaFieldLabelComponent = LabelComponent<'textarea'>
|
export type TextareaFieldLabelComponent = LabelComponent<'textarea'>
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export type UploadFieldProps = {
|
|||||||
relationTo?: UploadField['relationTo']
|
relationTo?: UploadField['relationTo']
|
||||||
validate?: UploadFieldValidation
|
validate?: UploadFieldValidation
|
||||||
width?: string
|
width?: string
|
||||||
} & FormFieldBase
|
} & Omit<FormFieldBase, 'validate'>
|
||||||
|
|
||||||
export type UploadFieldLabelComponent = LabelComponent<'upload'>
|
export type UploadFieldLabelComponent = LabelComponent<'upload'>
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import type { TextFieldProps } from 'payload'
|
import type { SelectFieldValidation, TextFieldProps } from 'payload'
|
||||||
|
|
||||||
import { SelectField, useForm } from '@payloadcms/ui'
|
import { SelectField, useForm } from '@payloadcms/ui'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
|
|
||||||
import type { SelectFieldOption } from '../../types.js'
|
import type { SelectFieldOption } from '../../types.js'
|
||||||
|
|
||||||
export const DynamicFieldSelector: React.FC<TextFieldProps> = (props) => {
|
export const DynamicFieldSelector: React.FC<
|
||||||
|
{ validate: SelectFieldValidation } & TextFieldProps
|
||||||
|
> = (props) => {
|
||||||
const { fields, getDataByPath } = useForm()
|
const { fields, getDataByPath } = useForm()
|
||||||
|
|
||||||
const [options, setOptions] = useState<SelectFieldOption[]>([])
|
const [options, setOptions] = useState<SelectFieldOption[]>([])
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const RichTextField: React.FC<
|
|||||||
richTextComponentMap: Map<string, React.ReactNode>
|
richTextComponentMap: Map<string, React.ReactNode>
|
||||||
validate?: RichTextFieldValidation
|
validate?: RichTextFieldValidation
|
||||||
width?: string
|
width?: string
|
||||||
} & FormFieldBase
|
} & Omit<FormFieldBase, 'validate'>
|
||||||
> = (props) => {
|
> = (props) => {
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
|
|||||||
Reference in New Issue
Block a user