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",
|
||||
"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",
|
||||
"cwd": "${workspaceFolder}",
|
||||
|
||||
@@ -1,45 +1,103 @@
|
||||
'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 React from 'react'
|
||||
|
||||
type Props = {
|
||||
loginWithUsername?: LoginWithUsernameOptions | false
|
||||
}
|
||||
export const EmailAndUsernameFields: React.FC<Props> = ({ loginWithUsername }) => {
|
||||
function EmailFieldComponent(props: Props) {
|
||||
const { loginWithUsername } = props
|
||||
const { t } = useTranslation()
|
||||
|
||||
const requireEmail = !loginWithUsername || (loginWithUsername && loginWithUsername.requireEmail)
|
||||
const requireUsername = loginWithUsername && loginWithUsername.requireUsername
|
||||
const showEmailField =
|
||||
!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)
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{showEmailField && (
|
||||
<EmailField
|
||||
autoComplete="email"
|
||||
label={t('general:email')}
|
||||
name="email"
|
||||
path="email"
|
||||
required={requireEmail}
|
||||
validate={email}
|
||||
/>
|
||||
)}
|
||||
if (showUsernameField) {
|
||||
return (
|
||||
<TextField
|
||||
label={t('authentication:username')}
|
||||
name="username"
|
||||
path="username"
|
||||
required={requireUsername}
|
||||
validate={username}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
{showUsernameField && (
|
||||
<TextField
|
||||
label={t('authentication:username')}
|
||||
name="username"
|
||||
path="username"
|
||||
required={requireUsername}
|
||||
validate={username}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
return null
|
||||
}
|
||||
|
||||
type RenderEmailAndUsernameFieldsProps = {
|
||||
className?: string
|
||||
loginWithUsername?: LoginWithUsernameOptions | false
|
||||
operation?: 'create' | 'update'
|
||||
permissions?: {
|
||||
[fieldName: string]: FieldPermissions
|
||||
}
|
||||
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 React from 'react'
|
||||
|
||||
import { EmailAndUsernameFields } from '../../elements/EmailAndUsername/index.js'
|
||||
import { RenderEmailAndUsernameFields } from '../../elements/EmailAndUsername/index.js'
|
||||
|
||||
export const CreateFirstUserClient: React.FC<{
|
||||
initialState: FormState
|
||||
@@ -57,7 +57,12 @@ export const CreateFirstUserClient: React.FC<{
|
||||
redirect={admin}
|
||||
validationOperation="create"
|
||||
>
|
||||
<EmailAndUsernameFields loginWithUsername={loginWithUsername} />
|
||||
<RenderEmailAndUsernameFields
|
||||
className="emailAndUsername"
|
||||
loginWithUsername={loginWithUsername}
|
||||
operation="create"
|
||||
readOnly={false}
|
||||
/>
|
||||
<PasswordField
|
||||
label={t('authentication:newPassword')}
|
||||
name="password"
|
||||
|
||||
@@ -3,3 +3,7 @@
|
||||
margin-bottom: var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.emailAndUsername {
|
||||
margin-bottom: var(--base);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import { toast } from 'sonner'
|
||||
|
||||
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 './index.scss'
|
||||
|
||||
@@ -47,7 +47,7 @@ export const Auth: React.FC<Props> = (props) => {
|
||||
const dispatchFields = useFormFields((reducer) => reducer[1])
|
||||
const modified = useFormModified()
|
||||
const { i18n, t } = useTranslation()
|
||||
const { isInitializing } = useDocumentInfo()
|
||||
const { docPermissions, isInitializing } = useDocumentInfo()
|
||||
|
||||
const {
|
||||
routes: { api },
|
||||
@@ -138,7 +138,12 @@ export const Auth: React.FC<Props> = (props) => {
|
||||
<div className={[baseClass, className].filter(Boolean).join(' ')}>
|
||||
{!disableLocalStrategy && (
|
||||
<React.Fragment>
|
||||
<EmailAndUsernameFields loginWithUsername={loginWithUsername} />
|
||||
<RenderEmailAndUsernameFields
|
||||
loginWithUsername={loginWithUsername}
|
||||
operation={operation}
|
||||
permissions={docPermissions?.fields}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
{(showPasswordFields || requirePassword) && (
|
||||
<div className={`${baseClass}__changing-password`}>
|
||||
<PasswordField
|
||||
|
||||
@@ -15,7 +15,7 @@ export type ArrayFieldProps = {
|
||||
name?: string
|
||||
validate?: ArrayFieldValidation
|
||||
width?: string
|
||||
} & FormFieldBase
|
||||
} & Omit<FormFieldBase, 'validate'>
|
||||
|
||||
export type ArrayFieldLabelComponent = LabelComponent<'array'>
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ export type BlocksFieldProps = {
|
||||
slug?: string
|
||||
validate?: BlockFieldValidation
|
||||
width?: string
|
||||
} & FormFieldBase
|
||||
} & Omit<FormFieldBase, 'validate'>
|
||||
|
||||
export type ReducedBlock = {
|
||||
LabelComponent: Block['admin']['components']['Label']
|
||||
|
||||
@@ -12,7 +12,7 @@ export type CheckboxFieldProps = {
|
||||
path?: string
|
||||
validate?: CheckboxFieldValidation
|
||||
width?: string
|
||||
} & FormFieldBase
|
||||
} & Omit<FormFieldBase, 'validate'>
|
||||
|
||||
export type CheckboxFieldLabelComponent = LabelComponent<'checkbox'>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export type CodeFieldProps = {
|
||||
path?: string
|
||||
validate?: CodeFieldValidation
|
||||
width: string
|
||||
} & FormFieldBase
|
||||
} & Omit<FormFieldBase, 'validate'>
|
||||
|
||||
export type CodeFieldLabelComponent = LabelComponent<'code'>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export type DateFieldProps = {
|
||||
placeholder?: DateField['admin']['placeholder'] | string
|
||||
validate?: DateFieldValidation
|
||||
width?: string
|
||||
} & FormFieldBase
|
||||
} & Omit<FormFieldBase, 'validate'>
|
||||
|
||||
export type DateFieldLabelComponent = LabelComponent<'date'>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export type EmailFieldProps = {
|
||||
placeholder?: EmailField['admin']['placeholder']
|
||||
validate?: EmailFieldValidation
|
||||
width?: string
|
||||
} & FormFieldBase
|
||||
} & Omit<FormFieldBase, 'validate'>
|
||||
|
||||
export type EmailFieldLabelComponent = LabelComponent<'email'>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export type JSONFieldProps = {
|
||||
path?: string
|
||||
validate?: JSONFieldValidation
|
||||
width?: string
|
||||
} & FormFieldBase
|
||||
} & Omit<FormFieldBase, 'validate'>
|
||||
|
||||
export type JSONFieldLabelComponent = LabelComponent<'json'>
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ export type NumberFieldProps = {
|
||||
step?: number
|
||||
validate?: NumberFieldValidation
|
||||
width?: string
|
||||
} & FormFieldBase
|
||||
} & Omit<FormFieldBase, 'validate'>
|
||||
|
||||
export type NumberFieldLabelComponent = LabelComponent<'number'>
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export type PointFieldProps = {
|
||||
step?: number
|
||||
validate?: PointFieldValidation
|
||||
width?: string
|
||||
} & FormFieldBase
|
||||
} & Omit<FormFieldBase, 'validate'>
|
||||
|
||||
export type PointFieldLabelComponent = LabelComponent<'point'>
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export type RadioFieldProps = {
|
||||
validate?: RadioFieldValidation
|
||||
value?: string
|
||||
width?: string
|
||||
} & FormFieldBase
|
||||
} & Omit<FormFieldBase, 'validate'>
|
||||
|
||||
export type OnChange<T = string> = (value: T) => void
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export type RelationshipFieldProps = {
|
||||
sortOptions?: RelationshipField['admin']['sortOptions']
|
||||
validate?: RelationshipFieldValidation
|
||||
width?: string
|
||||
} & FormFieldBase
|
||||
} & Omit<FormFieldBase, 'validate'>
|
||||
|
||||
export type RelationshipFieldLabelComponent = LabelComponent<'relationship'>
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ export type RichTextComponentProps = {
|
||||
richTextComponentMap?: Map<string, MappedField[] | React.ReactNode>
|
||||
validate?: RichTextFieldValidation
|
||||
width?: string
|
||||
} & FormFieldBase
|
||||
} & Omit<FormFieldBase, 'validate'>
|
||||
|
||||
export type RichTextFieldLabelComponent = LabelComponent<'richText'>
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export type SelectFieldProps = {
|
||||
validate?: SelectFieldValidation
|
||||
value?: string
|
||||
width?: string
|
||||
} & FormFieldBase
|
||||
} & Omit<FormFieldBase, 'validate'>
|
||||
|
||||
export type SelectFieldLabelComponent = LabelComponent<'select'>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export type TextFieldProps = {
|
||||
placeholder?: TextField['admin']['placeholder']
|
||||
validate?: TextFieldValidation
|
||||
width?: string
|
||||
} & FormFieldBase
|
||||
} & Omit<FormFieldBase, 'validate'>
|
||||
|
||||
export type TextFieldLabelComponent = LabelComponent<'text'>
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export type TextareaFieldProps = {
|
||||
rows?: number
|
||||
validate?: TextareaFieldValidation
|
||||
width?: string
|
||||
} & FormFieldBase
|
||||
} & Omit<FormFieldBase, 'validate'>
|
||||
|
||||
export type TextareaFieldLabelComponent = LabelComponent<'textarea'>
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ export type UploadFieldProps = {
|
||||
relationTo?: UploadField['relationTo']
|
||||
validate?: UploadFieldValidation
|
||||
width?: string
|
||||
} & FormFieldBase
|
||||
} & Omit<FormFieldBase, 'validate'>
|
||||
|
||||
export type UploadFieldLabelComponent = LabelComponent<'upload'>
|
||||
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
'use client'
|
||||
|
||||
import type { TextFieldProps } from 'payload'
|
||||
import type { SelectFieldValidation, TextFieldProps } from 'payload'
|
||||
|
||||
import { SelectField, useForm } from '@payloadcms/ui'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
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 [options, setOptions] = useState<SelectFieldOption[]>([])
|
||||
|
||||
@@ -58,7 +58,7 @@ const RichTextField: React.FC<
|
||||
richTextComponentMap: Map<string, React.ReactNode>
|
||||
validate?: RichTextFieldValidation
|
||||
width?: string
|
||||
} & FormFieldBase
|
||||
} & Omit<FormFieldBase, 'validate'>
|
||||
> = (props) => {
|
||||
const {
|
||||
name,
|
||||
|
||||
Reference in New Issue
Block a user