Skip to content

Commit a53c1d5

Browse files
authored
fix: hidden and disabled fields cause incorrect field paths (#9680)
1 parent fce210b commit a53c1d5

File tree

26 files changed

+303
-51
lines changed

26 files changed

+303
-51
lines changed

docs/admin/fields.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ The following options are available:
5050
| **`style`** | [CSS Properties](https://developer.mozilla.org/en-US/docs/Web/CSS) to inject into the root element of the field. |
5151
| **`className`** | Attach a [CSS class attribute](https://developer.mozilla.org/en-US/docs/Web/CSS/Class_selectors) to the root DOM element of a field. |
5252
| **`readOnly`** | Setting a field to `readOnly` has no effect on the API whatsoever but disables the admin component's editability to prevent editors from modifying the field's value. |
53-
| **`disabled`** | If a field is `disabled`, it is completely omitted from the [Admin Panel](../admin/overview). |
53+
| **`disabled`** | If a field is `disabled`, it is completely omitted from the [Admin Panel](../admin/overview) entirely. |
5454
| **`disableBulkEdit`** | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. Defaults to `true` for UI fields. |
5555
| **`disableListColumn`** | Set `disableListColumn` to `true` to prevent fields from appearing in the list view column selector. |
5656
| **`disableListFilter`** | Set `disableListFilter` to `true` to prevent fields from appearing in the list view filter options. |

packages/next/src/views/Version/RenderFieldsToDiff/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22
import type { DiffMethod } from 'react-diff-viewer-continued'
33

4-
import { fieldAffectsData } from 'payload/shared'
4+
import { fieldAffectsData, fieldIsID } from 'payload/shared'
55
import React from 'react'
66

77
import type { diffComponents as _diffComponents } from './fields/index.js'
@@ -29,7 +29,7 @@ const RenderFieldsToDiff: React.FC<Props> = ({
2929
return (
3030
<div className={baseClass}>
3131
{fields?.map((field, i) => {
32-
if ('name' in field && field.name === 'id') {
32+
if (fieldIsID(field)) {
3333
return null
3434
}
3535

packages/payload/src/collections/config/client.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,6 @@ export const createClientCollectionConfig = ({
234234
}
235235
break
236236

237-
break
238237
default:
239238
clientCollection[key] = collection[key]
240239
}

packages/payload/src/exports/shared.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export {
1919
fieldIsArrayType,
2020
fieldIsBlockType,
2121
fieldIsGroupType,
22+
fieldIsHiddenOrDisabled,
23+
fieldIsID,
2224
fieldIsLocalized,
2325
fieldIsPresentationalOnly,
2426
fieldIsSidebar,

packages/payload/src/fields/config/client.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,28 +75,24 @@ export const createClientField = ({
7575
}): ClientField => {
7676
const clientField: ClientField = {} as ClientField
7777

78-
const isHidden = 'hidden' in incomingField && incomingField?.hidden
79-
const disabledFromAdmin =
80-
incomingField?.admin && 'disabled' in incomingField.admin && incomingField.admin.disabled
81-
82-
if (fieldAffectsData(incomingField) && (isHidden || disabledFromAdmin)) {
83-
return null
84-
}
85-
8678
for (const key in incomingField) {
8779
if (serverOnlyFieldProperties.includes(key as any)) {
8880
continue
8981
}
82+
9083
switch (key) {
9184
case 'admin':
9285
if (!incomingField.admin) {
9386
break
9487
}
88+
9589
clientField.admin = {} as AdminClient
90+
9691
for (const adminKey in incomingField.admin) {
9792
if (serverOnlyFieldAdminProperties.includes(adminKey as any)) {
9893
continue
9994
}
95+
10096
switch (adminKey) {
10197
case 'description':
10298
if ('description' in incomingField.admin) {
@@ -107,16 +103,20 @@ export const createClientField = ({
107103
}
108104

109105
break
106+
110107
default:
111108
clientField.admin[adminKey] = incomingField.admin[adminKey]
112109
}
113110
}
111+
114112
break
113+
115114
case 'blocks':
116115
case 'fields':
117116
case 'tabs':
118117
// Skip - we handle sub-fields in the switch below
119118
break
119+
120120
case 'label':
121121
//@ts-expect-error - would need to type narrow
122122
if (typeof incomingField.label === 'function') {
@@ -126,7 +126,9 @@ export const createClientField = ({
126126
//@ts-expect-error - would need to type narrow
127127
clientField.label = incomingField.label
128128
}
129+
129130
break
131+
130132
default:
131133
clientField[key] = incomingField[key]
132134
}
@@ -243,6 +245,7 @@ export const createClientField = ({
243245

244246
break
245247
}
248+
246249
case 'richText': {
247250
if (!incomingField?.editor) {
248251
throw new MissingEditorProp(incomingField) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
@@ -269,6 +272,7 @@ export const createClientField = ({
269272
if (serverOnlyFieldProperties.includes(key as any)) {
270273
continue
271274
}
275+
272276
if (key === 'fields') {
273277
clientTab.fields = createClientFields({
274278
defaultIDType,
@@ -320,9 +324,7 @@ export const createClientFields = ({
320324
importMap,
321325
})
322326

323-
if (clientField) {
324-
clientFields.push(clientField)
325-
}
327+
clientFields.push(clientField)
326328
}
327329

328330
const hasID = flattenTopLevelFields(fields).some((f) => fieldAffectsData(f) && f.name === 'id')

packages/payload/src/fields/config/types.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,6 +1709,21 @@ export function fieldIsSidebar<TField extends ClientField | Field | TabAsField |
17091709
return 'admin' in field && 'position' in field.admin && field.admin.position === 'sidebar'
17101710
}
17111711

1712+
export function fieldIsID<TField extends ClientField | Field>(
1713+
field: TField,
1714+
): field is { name: 'id' } & TField {
1715+
return 'name' in field && field.name === 'id'
1716+
}
1717+
1718+
export function fieldIsHiddenOrDisabled<
1719+
TField extends ClientField | Field | TabAsField | TabAsFieldClient,
1720+
>(field: TField): field is { admin: { hidden: true } } & TField {
1721+
return (
1722+
('hidden' in field && field.hidden) ||
1723+
('admin' in field && 'disabled' in field.admin && field.admin.disabled)
1724+
)
1725+
}
1726+
17121727
export function fieldAffectsData<
17131728
TField extends ClientField | Field | TabAsField | TabAsFieldClient,
17141729
>(

packages/payload/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,7 @@ export class BasePayload {
590590
if (!fieldAffectsData(field)) {
591591
return
592592
}
593+
593594
if (field.name === 'id') {
594595
customIDType = field.type
595596
return true

packages/payload/src/utilities/fieldSchemaToJSON.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ export const fieldSchemaToJSON = (fields: ClientField[]): FieldSchemaJSON => {
5252

5353
break
5454

55-
case 'collapsible':
56-
55+
case 'collapsible': // eslint-disable no-fallthrough
5756
case 'row':
5857
result = result.concat(fieldSchemaToJSON(field.fields))
5958
break
59+
6060
case 'group':
6161
acc.push({
6262
name: field.name,
@@ -66,8 +66,7 @@ export const fieldSchemaToJSON = (fields: ClientField[]): FieldSchemaJSON => {
6666

6767
break
6868

69-
case 'relationship':
70-
69+
case 'relationship': // eslint-disable no-fallthrough
7170
case 'upload':
7271
acc.push({
7372
name: field.name,
@@ -77,6 +76,7 @@ export const fieldSchemaToJSON = (fields: ClientField[]): FieldSchemaJSON => {
7776
})
7877

7978
break
79+
8080
case 'tabs': {
8181
let tabFields = []
8282

packages/ui/src/elements/FieldSelect/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22
import type { ClientField, FieldWithPath, FormState } from 'payload'
33

4-
import { fieldAffectsData, fieldHasSubFields } from 'payload/shared'
4+
import { fieldAffectsData, fieldHasSubFields, fieldIsHiddenOrDisabled } from 'payload/shared'
55
import React, { Fragment, useState } from 'react'
66

77
import { RenderCustomComponent } from '../../elements/RenderCustomComponent/index.js'
@@ -69,7 +69,7 @@ const reduceFields = ({
6969
(fieldAffectsData(field) || field.type === 'ui') &&
7070
(field.admin.disableBulkEdit ||
7171
field.unique ||
72-
field.admin.hidden ||
72+
fieldIsHiddenOrDisabled(field) ||
7373
('readOnly' in field && field.readOnly))
7474
) {
7575
return fieldsToUse

packages/ui/src/elements/Table/DefaultCell/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { DefaultCellComponentProps, UploadFieldClient } from 'payload'
33

44
import { getTranslation } from '@payloadcms/translations'
55
import LinkImport from 'next/link.js'
6-
import { fieldAffectsData } from 'payload/shared'
6+
import { fieldAffectsData, fieldIsID } from 'payload/shared'
77
import React from 'react' // TODO: abstract this out to support all routers
88

99
import { useConfig } from '../../../providers/Config/index.js'
@@ -77,7 +77,7 @@ export const DefaultCell: React.FC<DefaultCellComponentProps> = (props) => {
7777
}
7878
}
7979

80-
if ('name' in field && field.name === 'id') {
80+
if (fieldIsID(field)) {
8181
return (
8282
<WrapElement {...wrapElementProps}>
8383
<CodeCell

0 commit comments

Comments
 (0)