Skip to content
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

feat(payload, ui): adds disableListColumn & disableListFilter to fields admin props #6238

Merged
merged 8 commits into from
May 10, 2024
28 changes: 15 additions & 13 deletions docs/fields/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -163,19 +163,21 @@ Example:

In addition to each field's base configuration, you can define specific traits and properties for fields that only have effect on how they are rendered in the Admin panel. The following properties are available for all fields within the `admin` property:

| Option | Description |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `condition` | You can programmatically show / hide fields based on what other fields are doing. [Click here](#conditional-logic) for more info. |
| `components` | All field components can be completely and easily swapped out for custom components that you define. [Click here](#custom-components) for more info. |
| `description` | Helper text to display with the field to provide more information for the editor user. [Click here](#description) for more info. |
| `position` | Specify if the field should be rendered in the sidebar by defining `position: 'sidebar'`. |
| `width` | Restrict the width of a field. you can pass any string-based value here, be it pixels, percentages, etc. This property is especially useful when fields are nested within a `Row` type where they can be organized horizontally. |
| `style` | Attach raw CSS style properties to the root DOM element of a field. |
| `className` | Attach a CSS class name to the root DOM element of a field. |
| `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. |
| `disabled` | If a field is `disabled`, it is completely omitted from the Admin panel. |
| `disableBulkEdit` | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. |
| `hidden` | Setting a field's `hidden` property on its `admin` config will transform it into a `hidden` input type. Its value will still submit with the Admin panel's requests, but the field itself will not be visible to editors. |
| Option | Description |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `condition` | You can programmatically show / hide fields based on what other fields are doing. [Click here](#conditional-logic) for more info. |
| `components` | All field components can be completely and easily swapped out for custom components that you define. [Click here](#custom-components) for more info. |
| `description` | Helper text to display with the field to provide more information for the editor user. [Click here](#description) for more info. |
| `position` | Specify if the field should be rendered in the sidebar by defining `position: 'sidebar'`. |
| `width` | Restrict the width of a field. you can pass any string-based value here, be it pixels, percentages, etc. This property is especially useful when fields are nested within a `Row` type where they can be organized horizontally. |
| `style` | Attach raw CSS style properties to the root DOM element of a field. |
| `className` | Attach a CSS class name to the root DOM element of a field. |
| `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. |
| `disabled` | If a field is `disabled`, it is completely omitted from the Admin panel. |
| `disableBulkEdit` | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. |
| `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` | Setting a field's `hidden` property on its `admin` config will transform it into a `hidden` input type. Its value will still submit with the Admin panel's requests, but the field itself will not be visible to editors. |

### Custom components

Expand Down
2 changes: 2 additions & 0 deletions packages/payload/src/fields/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export const baseAdminFields = joi.object().keys({
.alternatives()
.try(joi.string(), joi.object().pattern(joi.string(), [joi.string()]), componentSchema),
disableBulkEdit: joi.boolean().default(false),
disableListColumn: joi.boolean().default(false),
disableListFilter: joi.boolean().default(false),
disabled: joi.boolean().default(false),
hidden: joi.boolean().default(false),
initCollapsed: joi.boolean().default(false),
Expand Down
4 changes: 4 additions & 0 deletions packages/payload/src/fields/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ type Admin = {
custom?: Record<string, any>
description?: Description
disableBulkEdit?: boolean
disableListColumn?: boolean
disableListFilter?: boolean
disabled?: boolean
hidden?: boolean
position?: 'sidebar'
Expand Down Expand Up @@ -428,6 +430,8 @@ export type UIField = {
/** Extension point to add your custom data. Available in server and client. */
custom?: Record<string, any>
disableBulkEdit?: boolean
disableListColumn?: boolean
disableListFilter?: boolean
position?: string
width?: string
}
Expand Down
14 changes: 12 additions & 2 deletions packages/ui/src/elements/ColumnSelector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import type { SanitizedCollectionConfig } from 'payload/types'

import React, { useId } from 'react'

import type { Column } from '../Table/index.js'

import { Plus } from '../../icons/Plus/index.js'
import { X } from '../../icons/X/index.js'
import { useEditDepth } from '../../providers/EditDepth/index.js'
Expand All @@ -17,6 +19,12 @@ export type Props = {
collectionSlug: SanitizedCollectionConfig['slug']
}

const filterColumnFields = (fields: Column[]): Column[] => {
return fields.filter((field) => {
return !field.admin?.disableListColumn
})
}

export const ColumnSelector: React.FC<Props> = ({ collectionSlug }) => {
const { columns, moveColumn, toggleColumn } = useTableColumns()

Expand All @@ -27,18 +35,20 @@ export const ColumnSelector: React.FC<Props> = ({ collectionSlug }) => {
return null
}

const filteredColumns = filterColumnFields(columns)

return (
<DraggableSortable
className={baseClass}
ids={columns.map((col) => col?.accessor)}
ids={filteredColumns.map((col) => col?.accessor)}
onDragEnd={({ moveFromIndex, moveToIndex }) => {
moveColumn({
fromIndex: moveFromIndex,
toIndex: moveToIndex,
})
}}
>
{columns.map((col, i) => {
{filteredColumns.map((col, i) => {
if (!col) return null

const { Label, accessor, active } = col
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/elements/Table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export type Column = {
Label: React.ReactNode
accessor: string
active: boolean
admin?: FieldBase['admin']
cellProps?: Partial<CellComponentProps>
components: {
Cell: React.ReactNode
Expand Down
4 changes: 4 additions & 0 deletions packages/ui/src/elements/TableColumns/buildColumnState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ export const buildColumnState = (args: Args): Column[] => {
Label,
accessor: name,
active,
admin: {
disableListColumn: field.disableListColumn,
disableListFilter: field.disableListFilter,
},
cellProps: {
...field.cellComponentProps,
...cellProps?.[index],
Expand Down
2 changes: 2 additions & 0 deletions packages/ui/src/elements/WhereBuilder/reduceFieldMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export const reduceFieldMap = (fieldMap: Column[], i18n) =>
},
}

if (field.admin?.disableListFilter) return reduced

return [...reduced, formattedField]
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,10 @@ export const mapFields = (args: {
custom: field?.admin?.custom,
disableBulkEdit:
'admin' in field && 'disableBulkEdit' in field.admin && field.admin.disableBulkEdit,
disableListColumn:
'admin' in field && 'disableListColumn' in field.admin && field.admin.disableListColumn,
disableListFilter:
'admin' in field && 'disableListFilter' in field.admin && field.admin.disableListFilter,
fieldComponentProps,
fieldIsPresentational,
isFieldAffectingData,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ export type MappedField = {
cellComponentProps: CellComponentProps
custom?: Record<any, string>
disableBulkEdit?: boolean
disableListColumn?: boolean
disableListFilter?: boolean
disabled?: boolean
fieldComponentProps: FieldComponentProps
fieldIsPresentational: boolean
Expand Down
16 changes: 16 additions & 0 deletions test/fields/collections/Text/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,22 @@ const TextFields: CollectionConfig = {
hasMany: true,
maxRows: 4,
},
{
name: 'disableListColumnText',
type: 'text',
admin: {
disableListColumn: true,
disableListFilter: false,
},
},
{
name: 'disableListFilterText',
type: 'text',
admin: {
disableListColumn: false,
disableListFilter: true,
},
},
],
}

Expand Down
2 changes: 2 additions & 0 deletions test/fields/collections/Text/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export const textFieldsSlug = 'text-fields'
export const textDoc: Partial<TextField> = {
text: 'Seeded text document',
localizedText: 'Localized text',
disableListColumnText: 'Disable List Column Text',
disableListFilterText: 'Disable List Filter Text',
}

export const anotherTextDoc: Partial<TextField> = {
Expand Down
69 changes: 69 additions & 0 deletions test/fields/e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { Config } from './payload-types.js'

import {
ensureAutoLoginAndCompilationIsDone,
exactText,
initPageConsoleErrorCatch,
navigateToListCellLink,
openDocDrawer,
Expand Down Expand Up @@ -82,6 +83,74 @@ describe('fields', () => {
await expect(textCell).toHaveText(textDoc.text)
})

test('should not display field in list view column selector if admin.disableListColumn is true', async () => {
PatrikKozak marked this conversation as resolved.
Show resolved Hide resolved
await page.goto(url.list)
await page.locator('.list-controls__toggle-columns').click()

await expect(page.locator('.column-selector')).toBeVisible()

// Check if "Disable List Column Text" is not present in the column options
await expect(
page.locator(`.column-selector .column-selector__column`, {
hasText: exactText('Disable List Column Text'),
}),
).toBeHidden()
})

test('should display field in list view filter selector if admin.disableListColumn is true and admin.disableListFilter is false', async () => {
PatrikKozak marked this conversation as resolved.
Show resolved Hide resolved
await page.goto(url.list)
await page.locator('.list-controls__toggle-where').click()
await page.waitForSelector('.list-controls__where.rah-static--height-auto')
await page.locator('.where-builder__add-first-filter').click()

const initialField = page.locator('.condition__field')
await initialField.click()
const initialFieldOptions = initialField.locator('.rs__option')

// Get all text contents of options
const optionsTexts = await initialFieldOptions.allTextContents()

// Check if any option text contains "Disable List Column Text"
const containsText = optionsTexts.some((text) => text.includes('Disable List Column Text'))

// Assert that at least one option contains the desired text
expect(containsText).toBeTruthy()
PatrikKozak marked this conversation as resolved.
Show resolved Hide resolved
})

test('should display field in list view column selector if admin.disableListColumn is false and admin.disableListFilter is true', async () => {
await page.goto(url.list)
await page.locator('.list-controls__toggle-columns').click()

await expect(page.locator('.column-selector')).toBeVisible()

// Check if "Disable List Filter Text" is present in the column options
await expect(
page.locator(`.column-selector .column-selector__column`, {
hasText: exactText('Disable List Filter Text'),
}),
).toBeVisible()
})

test('should not display field in list view filter condition selector if admin.disableListFilter is true', async () => {
PatrikKozak marked this conversation as resolved.
Show resolved Hide resolved
await page.goto(url.list)
await page.locator('.list-controls__toggle-where').click()
await page.waitForSelector('.list-controls__where.rah-static--height-auto')
await page.locator('.where-builder__add-first-filter').click()

const initialField = page.locator('.condition__field')
await initialField.click()
const initialFieldOptions = initialField.locator('.rs__option')

// Get all text contents of options
const optionsTexts = await initialFieldOptions.allTextContents()

// Check if any option text contains "Disable List Filter Text"
const containsText = optionsTexts.some((text) => text.includes('Disable List Filter Text'))

// Assert that none of the options contain the desired text
expect(containsText).toBeFalsy()
PatrikKozak marked this conversation as resolved.
Show resolved Hide resolved
})

test('should display i18n label in cells when missing field data', async () => {
await page.goto(url.list)
const textCell = page.locator('.row-1 .cell-i18nText')
Expand Down
2 changes: 2 additions & 0 deletions test/fields/payload-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,8 @@ export interface TextField {
localizedHasMany?: string[] | null
withMinRows?: string[] | null
withMaxRows?: string[] | null
disableListColumnText?: string | null
disableListFilterText?: string | null
updatedAt: string
createdAt: string
}
Expand Down
Loading