feat: join field admin.defaultColumns (#9982)

Add the ability to specify which columns should appear in the
relationship table of a join fields

The new property is in the Join field `admin.defaultColumns` and can be
set to an array of strings containing the field names in the desired
order.
This commit is contained in:
Dan Ribbens
2024-12-14 22:31:31 -05:00
committed by GitHub
parent f5516b96da
commit 2ec4d0c2ef
6 changed files with 42 additions and 16 deletions

View File

@@ -145,10 +145,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 |
|------------------------|----------------------------------------------------------------------------------------| |------------------------|---------------------------------------------------------------------------------------------------------------------------|
| **`allowCreate`** | Set to `false` to remove the controls for making new related documents from this field. | | **`defaultColumns`** | Array of field names that correspond to which columns to show in the relationship table. Default is the collection config. |
| **`components.Label`** | Override the default Label of the Field Component. [More details](../admin/fields#label) | | **`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](../admin/fields#label) |
## Join Field Data ## Join Field Data

View File

@@ -49,7 +49,7 @@ export type ListQuery = {
export type BuildTableStateArgs = { export type BuildTableStateArgs = {
collectionSlug: string collectionSlug: string
columns?: any[] // TODO: type this (comes from ui pkg) columns?: { accessor: string; active: boolean }[]
docs?: PaginatedDocs['docs'] docs?: PaginatedDocs['docs']
enableRowSelections?: boolean enableRowSelections?: boolean
query?: ListQuery query?: ListQuery

View File

@@ -1390,6 +1390,7 @@ export type JoinField = {
Error?: CustomComponent<JoinFieldErrorClientComponent | JoinFieldErrorServerComponent> Error?: CustomComponent<JoinFieldErrorClientComponent | JoinFieldErrorServerComponent>
Label?: CustomComponent<JoinFieldLabelClientComponent | JoinFieldLabelServerComponent> Label?: CustomComponent<JoinFieldLabelClientComponent | JoinFieldLabelServerComponent>
} & Admin['components'] } & Admin['components']
defaultColumns?: string[]
disableBulkEdit?: never disableBulkEdit?: never
readOnly?: never readOnly?: never
} & Admin } & Admin
@@ -1422,7 +1423,8 @@ export type JoinField = {
FieldGraphQLType FieldGraphQLType
export type JoinFieldClient = { export type JoinFieldClient = {
admin?: AdminClient & Pick<JoinField['admin'], 'allowCreate' | 'disableBulkEdit' | 'readOnly'> admin?: AdminClient &
Pick<JoinField['admin'], 'allowCreate' | 'defaultColumns' | 'disableBulkEdit' | 'readOnly'>
} & FieldBaseClient & } & FieldBaseClient &
Pick< Pick<
JoinField, JoinField,

View File

@@ -115,12 +115,21 @@ export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (pro
newQuery.where = hoistQueryParamsToAnd(newQuery.where, filterOptions) newQuery.where = hoistQueryParamsToAnd(newQuery.where, filterOptions)
} }
// map columns from string[] to ColumnPreferences
const defaultColumns = field.admin.defaultColumns
? field.admin.defaultColumns.map((accessor) => ({
accessor,
active: true,
}))
: undefined
const { const {
data: newData, data: newData,
state: newColumnState, state: newColumnState,
Table: NewTable, Table: NewTable,
} = await getTableState({ } = await getTableState({
collectionSlug: relationTo, collectionSlug: relationTo,
columns: defaultColumns,
docs, docs,
enableRowSelections: false, enableRowSelections: false,
query: newQuery, query: newQuery,
@@ -134,11 +143,12 @@ export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (pro
setIsLoadingTable(false) setIsLoadingTable(false)
}, },
[ [
query,
field.defaultLimit, field.defaultLimit,
field.defaultSort, field.defaultSort,
field.admin.defaultColumns,
collectionConfig.admin.pagination.defaultLimit, collectionConfig.admin.pagination.defaultLimit,
collectionConfig.defaultSort, collectionConfig.defaultSort,
query,
filterOptions, filterOptions,
getTableState, getTableState,
relationTo, relationTo,

View File

@@ -91,6 +91,9 @@ export const Categories: CollectionConfig = {
type: 'join', type: 'join',
collection: postsSlug, collection: postsSlug,
on: 'group.category', on: 'group.category',
admin: {
defaultColumns: ['id', 'createdAt', 'title'],
},
}, },
{ {
name: 'camelCasePosts', name: 'camelCasePosts',

View File

@@ -218,16 +218,28 @@ test.describe('Join Field', () => {
const titleAscButton = titleColumn.locator('button.sort-column__asc') const titleAscButton = titleColumn.locator('button.sort-column__asc')
await expect(titleAscButton).toBeVisible() await expect(titleAscButton).toBeVisible()
await titleAscButton.click() await titleAscButton.click()
await expect(joinField.locator('tbody tr:first-child td:nth-child(2)')).toHaveText( await expect(joinField.locator('tbody .row-1')).toContainText('Test Post 1')
'Test Post 1',
)
const titleDescButton = titleColumn.locator('button.sort-column__desc') const titleDescButton = titleColumn.locator('button.sort-column__desc')
await expect(titleDescButton).toBeVisible() await expect(titleDescButton).toBeVisible()
await titleDescButton.click() await titleDescButton.click()
await expect(joinField.locator('tbody tr:first-child td:nth-child(2)')).toHaveText( await expect(joinField.locator('tbody .row-1')).toContainText('Test Post 3')
'Test Post 3', })
)
test('should display relationship table with columns from admin.defaultColumns', async () => {
await page.goto(categoriesURL.edit(categoryID))
const joinField = page.locator('#field-group__relatedPosts.field-type.join')
const thead = joinField.locator('.relationship-table thead')
await expect(thead).toContainText('ID')
await expect(thead).toContainText('Created At')
await expect(thead).toContainText('Title')
const innerText = await thead.innerText()
// expect the order of columns to be 'ID', 'Created At', 'Title'
// eslint-disable-next-line payload/no-flaky-assertions
expect(innerText.indexOf('ID')).toBeLessThan(innerText.indexOf('Created At'))
// eslint-disable-next-line payload/no-flaky-assertions
expect(innerText.indexOf('Created At')).toBeLessThan(innerText.indexOf('Title'))
}) })
test('should update relationship table when new document is created', async () => { test('should update relationship table when new document is created', async () => {
@@ -276,9 +288,7 @@ test.describe('Join Field', () => {
await titleField.fill('Test Post 1 Updated') await titleField.fill('Test Post 1 Updated')
await drawer.locator('button[id="action-save"]').click() await drawer.locator('button[id="action-save"]').click()
await expect(drawer).toBeHidden() await expect(drawer).toBeHidden()
await expect(joinField.locator('tbody tr:first-child td:nth-child(2)')).toHaveText( await expect(joinField.locator('tbody .row-1')).toContainText('Test Post 1 Updated')
'Test Post 1 Updated',
)
}) })
test('should render empty relationship table when creating new document', async () => { test('should render empty relationship table when creating new document', async () => {