Skip to content

feat: hide fields in version view + admin.hiddenInVersionView #7724

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/fields/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ The following options are available:
| **`disableListColumn`** | Set `disableListColumn` to `true` to prevent fields from appearing in the list view column selector. |
| **`disableListFilter`** | Set `disableListFilter` to `true` to prevent fields from appearing in the list view filter options. |
| **`hidden`** | Will transform the field into a `hidden` input type. Its value will still submit with requests in the Admin Panel, but the field itself will not be visible to editors. |
| **`hiddenInVersionView`** | Set `hiddenInVersionView` to `true` to prevent fields from appearing in the version view. |

### Field Descriptions

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { fieldIsID, fieldShouldBeLocalized, getUniqueListBy, tabHasName } from '
import { diffMethods } from './fields/diffMethods.js'
import { diffComponents } from './fields/index.js'
import { getFieldPathsModified } from './utilities/getFieldPathsModified.js'
import { isFieldHiddenInVersionView } from './utilities/isFieldHiddenInVersionView.js'

export type BuildVersionFieldsArgs = {
clientSchemaMap: ClientFieldSchemaMap
Expand Down Expand Up @@ -77,7 +78,8 @@ export const buildVersionFields = ({
let fieldIndex = -1
for (const field of fields) {
fieldIndex++
if (fieldIsID(field)) {

if (isFieldHiddenInVersionView(field)) {
continue
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
import { countChangedFields, countChangedFieldsInRows } from './countChangedFields.js'
import { isFieldHiddenInVersionView } from './isFieldHiddenInVersionView.js'

import type { ClientField } from 'payload'

// Mock isFieldHiddenInVersionView with it's original implementation so we can
// replace it later in the hidden fields tests.
// (Couldn't use jest.spyOn directly due to "TypeError: Cannot redefine property")
jest.mock('./isFieldHiddenInVersionView', () => {
const actual = jest.requireActual('./isFieldHiddenInVersionView')
return {
...actual,
isFieldHiddenInVersionView: jest.fn(actual.isFieldHiddenInVersionView),
}
})

describe('countChangedFields', () => {
// locales can be undefined when not configured in payload.config.js
const locales = undefined
Expand Down Expand Up @@ -40,16 +53,20 @@ describe('countChangedFields', () => {
expect(result).toBe(2)
})

it('should not count the id field because it is not displayed in the version view', () => {
it('should not count changed fields when isFieldHiddenInVersionView() returns true', async () => {
// Make isFieldHiddenInVersionView return true for one of the fields
// jest.mocked() is used to correct the type of isFieldHiddenInVersionView
jest.mocked(isFieldHiddenInVersionView).mockImplementationOnce(() => true)

const fields: ClientField[] = [
{ name: 'id', type: 'text' },
{ name: 'a', type: 'text' },
{ name: 'b', type: 'text' },
]
const comparison = { id: 'original', a: 'original' }
const version = { id: 'changed', a: 'original' }
const comparison = { a: 'original', b: 'original' }
const version = { a: 'changed', b: 'changed' }

const result = countChangedFields({ comparison, fields, version, locales })
expect(result).toBe(0)
expect(result).toBe(1)
})

it('should count changed fields inside collapsible fields', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { fieldShouldBeLocalized } from 'payload/shared'

import { fieldHasChanges } from './fieldHasChanges.js'
import { getFieldsForRowComparison } from './getFieldsForRowComparison.js'
import { isFieldHiddenInVersionView } from './isFieldHiddenInVersionView.js'

type Args = {
comparison: unknown
Expand All @@ -29,10 +30,11 @@ export function countChangedFields({
let count = 0

fields.forEach((field) => {
// Don't count the id field since it is not displayed in the UI
if ('name' in field && field.name === 'id') {
// Don't count fields that are hidden in the version view
if (isFieldHiddenInVersionView(field)) {
return
}

const fieldType = field.type
switch (fieldType) {
// Iterable fields are arrays and blocks fields. We iterate over each row and
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { ClientField, Field } from 'payload'

import { fieldIsID, fieldIsPresentationalOnly } from 'payload/shared'

export function isFieldHiddenInVersionView(field: ClientField | Field) {
// Presentational fields (like UI fields) are not supposed to appear in the
// version view. This narrows the type of field to avoid type errors.
if (fieldIsPresentationalOnly(field)) {
return true
}

// Don't render id fields
if (fieldIsID(field)) {
return true
}

// Don't render hidden fields, because they are supposed to be hidden everywhere
if (field.hidden) {
return true
}

// Don't render fields with admin.disabled, because they are supposed to be
// hidden everywhere in the admin UI.
if (field.admin?.disabled) {
return true
}

// Don't render fields with admin.hiddenInVersionView, because they are
// supposed to be hidden specifically in the version view.
if (field.admin?.hiddenInVersionView) {
return true
}

return false
}
10 changes: 10 additions & 0 deletions packages/payload/src/fields/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,11 @@ type Admin = {
*/
disableListFilter?: boolean
hidden?: boolean
/**
* Shows / hides fields in the version diff view.
* @type boolean
*/
hiddenInVersionView?: boolean
position?: 'sidebar'
readOnly?: boolean
style?: CSSProperties
Expand All @@ -372,6 +377,11 @@ export type AdminClient = {
*/
disableListFilter?: boolean
hidden?: boolean
/**
* Shows / hides fields in the version diff view.
* @type boolean
*/
hiddenInVersionView?: boolean
position?: 'sidebar'
readOnly?: boolean
style?: { '--field-width'?: CSSProperties['width'] } & CSSProperties
Expand Down
26 changes: 26 additions & 0 deletions test/versions/collections/Diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,32 @@ export const Diff: CollectionConfig = {
relationTo: 'media',
type: 'upload',
},
{
name: 'hidden',
type: 'text',
hidden: true,
},
{
name: 'adminHidden',
type: 'text',
admin: {
hidden: true,
},
},
{
name: 'adminDisabled',
type: 'text',
admin: {
disabled: true,
},
},
{
name: 'adminHiddenInVersionView',
type: 'text',
admin: {
hiddenInVersionView: true,
},
},
],
versions: {
maxPerDoc: 35,
Expand Down
41 changes: 39 additions & 2 deletions test/versions/e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1257,7 +1257,7 @@ describe('Versions', () => {

// Expect iterable change count to be visible
const iterableChangeCount = blocksDiff.locator('.diff-collapser__field-change-count').first()
await expect(iterableChangeCount).toHaveText('2 changed fields')
await expect(iterableChangeCount).toHaveText('1 changed field')

// Expect iterable rows to be visible
const blocksDiffRows = blocksDiff.locator('.iterable-diff__rows')
Expand All @@ -1271,7 +1271,7 @@ describe('Versions', () => {
const firstBlocksDiffRowChangeCount = firstBlocksDiffRow
.locator('.diff-collapser__field-change-count')
.first()
await expect(firstBlocksDiffRowChangeCount).toHaveText('2 changed fields')
await expect(firstBlocksDiffRowChangeCount).toHaveText('1 changed field')

// Expect collapser content to be visible
const diffCollapserContent = blocksDiffRows.locator('.diff-collapser__content')
Expand Down Expand Up @@ -1548,6 +1548,43 @@ describe('Versions', () => {
String(uploadDocs?.docs?.[1]?.id),
)
})

describe('hidden and disabled fields', () => {
test('should not render fields with hidden: true', async () => {
await navigateToVersionFieldsDiff()

// Fields with hidden should be hidden everywhere
const hiddenField = page.locator('[data-field-path="hidden"]')
await expect(hiddenField).toBeHidden()
})

test('should render fields with admin.hidden: true', async () => {
await navigateToVersionFieldsDiff()

// Fields with admin.hidden should be hidden in the edit view, but
// visible in the version view
const adminHiddenField = page.locator('[data-field-path="adminHidden"]')
await expect(adminHiddenField).toBeVisible()
})

test('should not render fields with admin.disabled: true', async () => {
await navigateToVersionFieldsDiff()

// Fields with admin.disabled should be hidden everywhere in the Admin UI
const adminDisabledField = page.locator('[data-field-path="adminDisabled"]')
await expect(adminDisabledField).toBeHidden()
})

test('should not render fields with admin.hiddenInVersionView: true', async () => {
await navigateToVersionFieldsDiff()

// Fields with admin.hiddenInVersionView should be hidden in the version view
const adminHiddenInVersionViewView = page.locator(
'[data-field-path="adminHiddenInVersionView"]',
)
await expect(adminHiddenInVersionViewView).toBeHidden()
})
})
})

describe('Scheduled publish', () => {
Expand Down