feat(ui): add support for disabling join field row types (#12738)
### What? This PR adds a new `admin.disableRowTypes` config to `'join'` fields which hides the `"Type"` column from the relationship table. ### Why? While the collection type column _can be_ helpful for providing information, it's not always necessary and can sometimes be redundant when the field only has a singular relationTo. Hiding it can be helpful by removing visual noise and providing editors the data directly. ### How? By threading `admin.disableRowTypes` directly to the `getTableState` function of the `RelationshipTable` component. **With row types** (via `admin.disableRowTypes: false | undefined` OR default for polymorphic):  **Without row types** (default for monomorphic): 
This commit is contained in:
@@ -158,10 +158,11 @@ _\* An asterisk denotes that a property is required._
|
|||||||
You can control the user experience of the join field using the `admin` config properties. The following options are supported:
|
You can control the user experience of the join field using the `admin` config properties. The following options are supported:
|
||||||
|
|
||||||
| Option | Description |
|
| Option | Description |
|
||||||
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| **`defaultColumns`** | Array of field names that correspond to which columns to show in the relationship table. Default is the collection config. |
|
| **`defaultColumns`** | Array of field names that correspond to which columns to show in the relationship table. Default is the collection config. |
|
||||||
| **`allowCreate`** | Set to `false` to remove the controls for making new related documents from this field. |
|
| **`allowCreate`** | Set to `false` to remove the controls for making new related documents from this field. |
|
||||||
| **`components.Label`** | Override the default Label of the Field Component. [More details](./overview#label) |
|
| **`components.Label`** | Override the default Label of the Field Component. [More details](./overview#label) |
|
||||||
|
| **`disableRowTypes`** | Set to `false` to render row types, and `true` to hide them. Defaults to `false` for join fields with a singular `relationTo`, and `true` for join fields where `relationTo` is an array. |
|
||||||
|
|
||||||
## Join Field Data
|
## Join Field Data
|
||||||
|
|
||||||
|
|||||||
@@ -1628,6 +1628,7 @@ export type JoinField = {
|
|||||||
} & Admin['components']
|
} & Admin['components']
|
||||||
defaultColumns?: string[]
|
defaultColumns?: string[]
|
||||||
disableBulkEdit?: never
|
disableBulkEdit?: never
|
||||||
|
disableRowTypes?: boolean
|
||||||
readOnly?: never
|
readOnly?: never
|
||||||
} & Admin
|
} & Admin
|
||||||
/**
|
/**
|
||||||
@@ -1679,8 +1680,11 @@ export type JoinField = {
|
|||||||
|
|
||||||
export type JoinFieldClient = {
|
export type JoinFieldClient = {
|
||||||
admin?: AdminClient &
|
admin?: AdminClient &
|
||||||
|
Pick<
|
||||||
|
JoinField['admin'],
|
||||||
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
|
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
|
||||||
Pick<JoinField['admin'], 'allowCreate' | 'defaultColumns' | 'disableBulkEdit' | 'readOnly'>
|
'allowCreate' | 'defaultColumns' | 'disableBulkEdit' | 'disableRowTypes' | 'readOnly'
|
||||||
|
>
|
||||||
} & { targetField: Pick<RelationshipFieldClient, 'relationTo'> } & FieldBaseClient &
|
} & { targetField: Pick<RelationshipFieldClient, 'relationTo'> } & FieldBaseClient &
|
||||||
Pick<
|
Pick<
|
||||||
JoinField,
|
JoinField,
|
||||||
|
|||||||
@@ -144,6 +144,11 @@ export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (pro
|
|||||||
}))
|
}))
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
|
const renderRowTypes =
|
||||||
|
typeof field.admin.disableRowTypes === 'boolean'
|
||||||
|
? !field.admin.disableRowTypes
|
||||||
|
: Array.isArray(relationTo)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: newData,
|
data: newData,
|
||||||
state: newColumnState,
|
state: newColumnState,
|
||||||
@@ -159,7 +164,7 @@ export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (pro
|
|||||||
: `_${field.collection}_${field.name}_order`,
|
: `_${field.collection}_${field.name}_order`,
|
||||||
parent,
|
parent,
|
||||||
query: newQuery,
|
query: newQuery,
|
||||||
renderRowTypes: true,
|
renderRowTypes,
|
||||||
tableAppearance: 'condensed',
|
tableAppearance: 'condensed',
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -172,6 +177,7 @@ export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (pro
|
|||||||
field.defaultLimit,
|
field.defaultLimit,
|
||||||
field.defaultSort,
|
field.defaultSort,
|
||||||
field.admin.defaultColumns,
|
field.admin.defaultColumns,
|
||||||
|
field.admin.disableRowTypes,
|
||||||
field.collection,
|
field.collection,
|
||||||
field.name,
|
field.name,
|
||||||
field.orderable,
|
field.orderable,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { ValidationError } from 'payload'
|
|||||||
|
|
||||||
import { categoriesSlug, hiddenPostsSlug, postsSlug } from '../shared.js'
|
import { categoriesSlug, hiddenPostsSlug, postsSlug } from '../shared.js'
|
||||||
import { singularSlug } from './Singular.js'
|
import { singularSlug } from './Singular.js'
|
||||||
|
import { versionsSlug } from './Versions.js'
|
||||||
|
|
||||||
export const Categories: CollectionConfig = {
|
export const Categories: CollectionConfig = {
|
||||||
slug: categoriesSlug,
|
slug: categoriesSlug,
|
||||||
@@ -55,6 +56,7 @@ export const Categories: CollectionConfig = {
|
|||||||
beforeInput: ['/components/BeforeInput.js#BeforeInput'],
|
beforeInput: ['/components/BeforeInput.js#BeforeInput'],
|
||||||
Description: '/components/CustomDescription/index.js#FieldDescriptionComponent',
|
Description: '/components/CustomDescription/index.js#FieldDescriptionComponent',
|
||||||
},
|
},
|
||||||
|
disableRowTypes: false,
|
||||||
},
|
},
|
||||||
collection: postsSlug,
|
collection: postsSlug,
|
||||||
defaultSort: '-title',
|
defaultSort: '-title',
|
||||||
@@ -62,6 +64,14 @@ export const Categories: CollectionConfig = {
|
|||||||
on: 'category',
|
on: 'category',
|
||||||
maxDepth: 1,
|
maxDepth: 1,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'noRowTypes',
|
||||||
|
type: 'join',
|
||||||
|
collection: postsSlug,
|
||||||
|
defaultLimit: 5,
|
||||||
|
on: 'category',
|
||||||
|
maxDepth: 1,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'hasManyPosts',
|
name: 'hasManyPosts',
|
||||||
type: 'join',
|
type: 'join',
|
||||||
@@ -95,6 +105,7 @@ export const Categories: CollectionConfig = {
|
|||||||
on: 'group.category',
|
on: 'group.category',
|
||||||
admin: {
|
admin: {
|
||||||
defaultColumns: ['id', 'createdAt', 'title'],
|
defaultColumns: ['id', 'createdAt', 'title'],
|
||||||
|
disableRowTypes: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -129,6 +140,21 @@ export const Categories: CollectionConfig = {
|
|||||||
collection: 'posts',
|
collection: 'posts',
|
||||||
on: 'blocks.category',
|
on: 'blocks.category',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'polymorphicJoin',
|
||||||
|
type: 'join',
|
||||||
|
collection: [postsSlug, versionsSlug],
|
||||||
|
on: 'category',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'polymorphicJoinNoRowTypes',
|
||||||
|
type: 'join',
|
||||||
|
collection: [postsSlug, versionsSlug],
|
||||||
|
on: 'category',
|
||||||
|
admin: {
|
||||||
|
disableRowTypes: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'polymorphic',
|
name: 'polymorphic',
|
||||||
type: 'join',
|
type: 'join',
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ export const Uploads: CollectionConfig = {
|
|||||||
type: 'join',
|
type: 'join',
|
||||||
collection: 'posts',
|
collection: 'posts',
|
||||||
on: 'upload',
|
on: 'upload',
|
||||||
|
admin: {
|
||||||
|
disableRowTypes: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
upload: {
|
upload: {
|
||||||
|
|||||||
@@ -277,7 +277,6 @@ export default buildConfigWithDefaults({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
slug: 'folders',
|
slug: 'folders',
|
||||||
fields: [
|
fields: [
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ describe('Join Field', () => {
|
|||||||
await saveDocAndAssert(page)
|
await saveDocAndAssert(page)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should render collection type in first column of relationship table', async () => {
|
test('should render collection type in first column of relationship table when disableRowTypes false', async () => {
|
||||||
await page.goto(categoriesURL.edit(categoryID))
|
await page.goto(categoriesURL.edit(categoryID))
|
||||||
const joinField = page.locator('#field-relatedPosts.field-type.join')
|
const joinField = page.locator('#field-relatedPosts.field-type.join')
|
||||||
await expect(joinField).toBeVisible()
|
await expect(joinField).toBeVisible()
|
||||||
@@ -237,7 +237,51 @@ describe('Join Field', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should render drawer toggler without document link in second column of relationship table', async () => {
|
test('should hide collection type column of monomorphic relationship table by default', async () => {
|
||||||
|
await page.goto(categoriesURL.edit(categoryID))
|
||||||
|
const joinField = page.locator('#field-noRowTypes.field-type.join')
|
||||||
|
const tableHeaderRow = joinField.locator('.table thead > tr')
|
||||||
|
const firstColumnHeader = tableHeaderRow.locator('th').first()
|
||||||
|
await expect(firstColumnHeader).toHaveId('heading-title')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should render collection type in first column of polymorphic relationship table by default', async () => {
|
||||||
|
await page.goto(categoriesURL.edit(categoryID))
|
||||||
|
const joinField = page.locator('#field-polymorphicJoin.field-type.join')
|
||||||
|
await expect(joinField).toBeVisible()
|
||||||
|
const text = joinField.locator('thead tr th#heading-collection:first-child')
|
||||||
|
await expect(text).toHaveText('Type')
|
||||||
|
const cells = joinField.locator('.relationship-table tbody tr td:first-child .pill__label')
|
||||||
|
|
||||||
|
const count = await cells.count()
|
||||||
|
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const element = cells.nth(i)
|
||||||
|
// Perform actions on each element
|
||||||
|
await expect(element).toBeVisible()
|
||||||
|
await expect(element).toHaveText('Post')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should not render collection type in polymorphic relationship table with disableRowTypes true', async () => {
|
||||||
|
await page.goto(categoriesURL.edit(categoryID))
|
||||||
|
const joinField = page.locator('#field-polymorphicJoinNoRowTypes.field-type.join')
|
||||||
|
await expect(joinField).toBeVisible()
|
||||||
|
const text = joinField.locator('thead tr th#heading-title:first-child')
|
||||||
|
await expect(text).toHaveText('Title')
|
||||||
|
const cells = joinField.locator('.relationship-table tbody tr td:first-child .pill__label')
|
||||||
|
|
||||||
|
const count = await cells.count()
|
||||||
|
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const element = cells.nth(i)
|
||||||
|
// Perform actions on each element
|
||||||
|
await expect(element).toBeVisible()
|
||||||
|
await expect(element).toHaveText(/Test Post \d+/)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should render drawer toggler without document link in second column of relationship table with row types', async () => {
|
||||||
await page.goto(categoriesURL.edit(categoryID))
|
await page.goto(categoriesURL.edit(categoryID))
|
||||||
const joinField = page.locator('#field-relatedPosts.field-type.join')
|
const joinField = page.locator('#field-relatedPosts.field-type.join')
|
||||||
await expect(joinField).toBeVisible()
|
await expect(joinField).toBeVisible()
|
||||||
|
|||||||
Reference in New Issue
Block a user