Skip to content

Commit 442189e

Browse files
fix: email and username fields rendering in drawers (#7520)
Fixes #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.
1 parent 5d1cc76 commit 442189e

File tree

23 files changed

+131
-50
lines changed

23 files changed

+131
-50
lines changed

.vscode/launch.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@
5656
"request": "launch",
5757
"type": "node-terminal"
5858
},
59+
{
60+
"command": "node --no-deprecation test/dev.js login-with-username",
61+
"cwd": "${workspaceFolder}",
62+
"name": "Run Dev Login-With-Username",
63+
"request": "launch",
64+
"type": "node-terminal"
65+
},
5966
{
6067
"command": "pnpm run dev plugin-cloud-storage",
6168
"cwd": "${workspaceFolder}",
Lines changed: 84 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,103 @@
11
'use client'
22

3-
import type { LoginWithUsernameOptions } from 'payload'
3+
import type { FieldPermissions, LoginWithUsernameOptions } from 'payload'
44

5-
import { EmailField, TextField, useTranslation } from '@payloadcms/ui'
5+
import { EmailField, RenderFields, TextField, useTranslation } from '@payloadcms/ui'
66
import { email, username } from 'payload/shared'
77
import React from 'react'
88

99
type Props = {
1010
loginWithUsername?: LoginWithUsernameOptions | false
1111
}
12-
export const EmailAndUsernameFields: React.FC<Props> = ({ loginWithUsername }) => {
12+
function EmailFieldComponent(props: Props) {
13+
const { loginWithUsername } = props
1314
const { t } = useTranslation()
1415

1516
const requireEmail = !loginWithUsername || (loginWithUsername && loginWithUsername.requireEmail)
16-
const requireUsername = loginWithUsername && loginWithUsername.requireUsername
1717
const showEmailField =
1818
!loginWithUsername || loginWithUsername?.requireEmail || loginWithUsername?.allowEmailLogin
19+
20+
if (showEmailField) {
21+
return (
22+
<EmailField
23+
autoComplete="off"
24+
label={t('general:email')}
25+
name="email"
26+
path="email"
27+
required={requireEmail}
28+
validate={email}
29+
/>
30+
)
31+
}
32+
33+
return null
34+
}
35+
36+
function UsernameFieldComponent(props: Props) {
37+
const { loginWithUsername } = props
38+
const { t } = useTranslation()
39+
40+
const requireUsername = loginWithUsername && loginWithUsername.requireUsername
1941
const showUsernameField = Boolean(loginWithUsername)
2042

43+
if (showUsernameField) {
44+
return (
45+
<TextField
46+
label={t('authentication:username')}
47+
name="username"
48+
path="username"
49+
required={requireUsername}
50+
validate={username}
51+
/>
52+
)
53+
}
54+
55+
return null
56+
}
57+
58+
type RenderEmailAndUsernameFieldsProps = {
59+
className?: string
60+
loginWithUsername?: LoginWithUsernameOptions | false
61+
operation?: 'create' | 'update'
62+
permissions?: {
63+
[fieldName: string]: FieldPermissions
64+
}
65+
readOnly: boolean
66+
}
67+
export function RenderEmailAndUsernameFields(props: RenderEmailAndUsernameFieldsProps) {
68+
const { className, loginWithUsername, operation, permissions, readOnly } = props
69+
2170
return (
22-
<React.Fragment>
23-
{showEmailField && (
24-
<EmailField
25-
autoComplete="email"
26-
label={t('general:email')}
27-
name="email"
28-
path="email"
29-
required={requireEmail}
30-
validate={email}
31-
/>
32-
)}
33-
34-
{showUsernameField && (
35-
<TextField
36-
label={t('authentication:username')}
37-
name="username"
38-
path="username"
39-
required={requireUsername}
40-
validate={username}
41-
/>
42-
)}
43-
</React.Fragment>
71+
<RenderFields
72+
className={className}
73+
fieldMap={[
74+
{
75+
name: 'email',
76+
type: 'text',
77+
CustomField: <EmailFieldComponent loginWithUsername={loginWithUsername} />,
78+
cellComponentProps: null,
79+
fieldComponentProps: { type: 'email', readOnly },
80+
fieldIsPresentational: false,
81+
isFieldAffectingData: true,
82+
localized: false,
83+
},
84+
{
85+
name: 'username',
86+
type: 'text',
87+
CustomField: <UsernameFieldComponent loginWithUsername={loginWithUsername} />,
88+
cellComponentProps: null,
89+
fieldComponentProps: { type: 'text', readOnly },
90+
fieldIsPresentational: false,
91+
isFieldAffectingData: true,
92+
localized: false,
93+
},
94+
]}
95+
forceRender
96+
operation={operation}
97+
path=""
98+
permissions={permissions}
99+
readOnly={readOnly}
100+
schemaPath=""
101+
/>
44102
)
45103
}

packages/next/src/views/CreateFirstUser/index.client.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
import { getFormState } from '@payloadcms/ui/shared'
1616
import React from 'react'
1717

18-
import { EmailAndUsernameFields } from '../../elements/EmailAndUsername/index.js'
18+
import { RenderEmailAndUsernameFields } from '../../elements/EmailAndUsername/index.js'
1919

2020
export const CreateFirstUserClient: React.FC<{
2121
initialState: FormState
@@ -57,7 +57,12 @@ export const CreateFirstUserClient: React.FC<{
5757
redirect={admin}
5858
validationOperation="create"
5959
>
60-
<EmailAndUsernameFields loginWithUsername={loginWithUsername} />
60+
<RenderEmailAndUsernameFields
61+
className="emailAndUsername"
62+
loginWithUsername={loginWithUsername}
63+
operation="create"
64+
readOnly={false}
65+
/>
6166
<PasswordField
6267
label={t('authentication:newPassword')}
6368
name="password"

packages/next/src/views/CreateFirstUser/index.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@
33
margin-bottom: var(--base);
44
}
55
}
6+
7+
.emailAndUsername {
8+
margin-bottom: var(--base);
9+
}

packages/next/src/views/Edit/Default/Auth/index.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { toast } from 'sonner'
1717

1818
import type { Props } from './types.js'
1919

20-
import { EmailAndUsernameFields } from '../../../../elements/EmailAndUsername/index.js'
20+
import { RenderEmailAndUsernameFields } from '../../../../elements/EmailAndUsername/index.js'
2121
import { APIKey } from './APIKey.js'
2222
import './index.scss'
2323

@@ -47,7 +47,7 @@ export const Auth: React.FC<Props> = (props) => {
4747
const dispatchFields = useFormFields((reducer) => reducer[1])
4848
const modified = useFormModified()
4949
const { i18n, t } = useTranslation()
50-
const { isInitializing } = useDocumentInfo()
50+
const { docPermissions, isInitializing } = useDocumentInfo()
5151

5252
const {
5353
routes: { api },
@@ -138,7 +138,12 @@ export const Auth: React.FC<Props> = (props) => {
138138
<div className={[baseClass, className].filter(Boolean).join(' ')}>
139139
{!disableLocalStrategy && (
140140
<React.Fragment>
141-
<EmailAndUsernameFields loginWithUsername={loginWithUsername} />
141+
<RenderEmailAndUsernameFields
142+
loginWithUsername={loginWithUsername}
143+
operation={operation}
144+
permissions={docPermissions?.fields}
145+
readOnly={readOnly}
146+
/>
142147
{(showPasswordFields || requirePassword) && (
143148
<div className={`${baseClass}__changing-password`}>
144149
<PasswordField

packages/payload/src/admin/fields/Array.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export type ArrayFieldProps = {
1515
name?: string
1616
validate?: ArrayFieldValidation
1717
width?: string
18-
} & FormFieldBase
18+
} & Omit<FormFieldBase, 'validate'>
1919

2020
export type ArrayFieldLabelComponent = LabelComponent<'array'>
2121

packages/payload/src/admin/fields/Blocks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export type BlocksFieldProps = {
1515
slug?: string
1616
validate?: BlockFieldValidation
1717
width?: string
18-
} & FormFieldBase
18+
} & Omit<FormFieldBase, 'validate'>
1919

2020
export type ReducedBlock = {
2121
LabelComponent: Block['admin']['components']['Label']

packages/payload/src/admin/fields/Checkbox.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export type CheckboxFieldProps = {
1212
path?: string
1313
validate?: CheckboxFieldValidation
1414
width?: string
15-
} & FormFieldBase
15+
} & Omit<FormFieldBase, 'validate'>
1616

1717
export type CheckboxFieldLabelComponent = LabelComponent<'checkbox'>
1818

packages/payload/src/admin/fields/Code.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export type CodeFieldProps = {
1010
path?: string
1111
validate?: CodeFieldValidation
1212
width: string
13-
} & FormFieldBase
13+
} & Omit<FormFieldBase, 'validate'>
1414

1515
export type CodeFieldLabelComponent = LabelComponent<'code'>
1616

packages/payload/src/admin/fields/Date.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export type DateFieldProps = {
1010
placeholder?: DateField['admin']['placeholder'] | string
1111
validate?: DateFieldValidation
1212
width?: string
13-
} & FormFieldBase
13+
} & Omit<FormFieldBase, 'validate'>
1414

1515
export type DateFieldLabelComponent = LabelComponent<'date'>
1616

0 commit comments

Comments
 (0)