fix(ui): populate nested fields for enableListViewSelectAPI (#13827)

Fixes an issue with the new experimental `enableListViewSelectAPI`
config option.

Group fields were not populating properly in the list view

### Before (incorrect)
```ts
{
  group.field: true
}
```

### After (correct)
```ts
{
  group: {
    field: true
  }
}
```
This commit is contained in:
Jarrod Flesch
2025-09-16 17:23:08 -04:00
committed by GitHub
parent a9553925f6
commit dc732b8f52
7 changed files with 142 additions and 54 deletions

View File

@@ -21,5 +21,15 @@ export const ListViewSelectAPI: CollectionConfig = {
name: 'description',
type: 'text',
},
{
name: 'group',
type: 'group',
fields: [
{
name: 'groupNameField',
type: 'text',
},
],
},
],
}

View File

@@ -36,6 +36,7 @@ let payload: PayloadTestSDK<Config>
import { listViewSelectAPISlug } from 'admin/collections/ListViewSelectAPI/index.js'
import { devUser } from 'credentials.js'
import { getRowByCellValueAndAssert } from 'helpers/e2e/getRowByCellValueAndAssert.js'
import {
openListColumns,
reorderColumns,
@@ -993,58 +994,101 @@ describe('List View', () => {
await toggleColumn(page, { columnLabel: 'ID', columnName: 'id', targetState: 'off' })
})
test('should use select API in the list view when `enableListViewSelectAPI` is true', async () => {
const doc = await payload.create({
collection: listViewSelectAPISlug,
data: {
title: 'This is a test title',
description: 'This is a test description',
},
describe('enableListViewSelectAPI', () => {
test('`id` should always be selected even when toggled off', async () => {
const doc = await payload.create({
collection: listViewSelectAPISlug,
data: {
title: 'This is a test title',
description: 'This is a test description',
},
})
const selectAPIUrl = new AdminUrlUtil(serverURL, listViewSelectAPISlug)
await page.goto(selectAPIUrl.list)
const printedResults = page.locator('#table-state')
await expect
.poll(
async () => {
const resultText = await printedResults.innerText()
const parsedResult = JSON.parse(resultText)
return Boolean(parsedResult[0].id && parsedResult[0].description)
},
{
timeout: 3000,
intervals: [100, 250, 500, 1000],
},
)
.toBeTruthy()
await toggleColumn(page, { columnLabel: 'ID', columnName: 'id', targetState: 'off' })
await toggleColumn(page, {
columnLabel: 'Description',
columnName: 'description',
targetState: 'off',
})
// Poll until the "description" field is removed from the response BUT `id` is still present
// The `id` field will remain selected despite it being inactive
await expect
.poll(
async () => {
const resultText = await printedResults.innerText()
const parsedResult = JSON.parse(resultText)
return Boolean(parsedResult[0].description === undefined && parsedResult[0].id)
},
{
timeout: 3000,
intervals: [100, 250, 500, 1000],
},
)
.toBeTruthy()
})
const selectAPIUrl = new AdminUrlUtil(serverURL, listViewSelectAPISlug)
await page.goto(selectAPIUrl.list)
const printedResults = page.locator('#table-state')
await expect
.poll(
async () => {
const resultText = await printedResults.innerText()
const parsedResult = JSON.parse(resultText)
return Boolean(parsedResult[0].id && parsedResult[0].description)
test('group fields should populate with the select API', async () => {
const doc = await payload.create({
collection: listViewSelectAPISlug,
data: {
title: 'This is a test title',
description: 'This is a test description',
group: {
groupNameField: 'Select Nested Field',
},
},
{
timeout: 3000,
intervals: [100, 250, 500, 1000],
},
)
.toBeTruthy()
})
await toggleColumn(page, { columnLabel: 'ID', columnName: 'id', targetState: 'off' })
const selectAPIUrl = new AdminUrlUtil(serverURL, listViewSelectAPISlug)
await toggleColumn(page, {
columnLabel: 'Description',
columnName: 'description',
targetState: 'off',
await page.goto(selectAPIUrl.list)
await toggleColumn(page, {
columnLabel: 'Group > Group Name Field',
columnName: 'group.groupNameField',
targetState: 'off',
})
await toggleColumn(page, {
columnLabel: 'Group > Group Name Field',
columnName: 'group.groupNameField',
targetState: 'on',
})
await getRowByCellValueAndAssert({
cellClass: `.cell-group__groupNameField`,
page,
textToMatch: 'Select Nested Field',
})
// cleanup after run
await payload.delete({
id: doc.id,
collection: listViewSelectAPISlug,
})
})
// Poll until the "description" field is removed from the response BUT `id` is still present
// The `id` field will remain selected despite it being inactive
await expect
.poll(
async () => {
const resultText = await printedResults.innerText()
const parsedResult = JSON.parse(resultText)
return Boolean(parsedResult[0].description === undefined && parsedResult[0].id)
},
{
timeout: 3000,
intervals: [100, 250, 500, 1000],
},
)
.toBeTruthy()
})
test('should toggle columns and save to preferences', async () => {

View File

@@ -594,6 +594,9 @@ export interface ListViewSelectApi {
id: string;
title?: string | null;
description?: string | null;
group?: {
groupNameField?: string | null;
};
updatedAt: string;
createdAt: string;
}
@@ -1153,6 +1156,11 @@ export interface CustomListDrawerSelect<T extends boolean = true> {
export interface ListViewSelectApiSelect<T extends boolean = true> {
title?: T;
description?: T;
group?:
| T
| {
groupNameField?: T;
};
updatedAt?: T;
createdAt?: T;
}

View File

@@ -76,6 +76,7 @@ export const testEslintConfig = [
'createFolderFromDoc',
'assertURLParams',
'uploadImage',
'getRowByCellValueAndAssert',
],
},
],

View File

@@ -0,0 +1,23 @@
import type { Locator, Page } from '@playwright/test'
import { expect } from '@playwright/test'
export async function getRowByCellValueAndAssert({
page,
textToMatch,
cellClass,
}: {
cellClass: `.cell-${string}`
page: Page
textToMatch: string
}): Promise<Locator> {
const row = page
.locator(`.collection-list .table tr`)
.filter({
has: page.locator(`${cellClass}`, { hasText: textToMatch }),
})
.first()
await expect(row).toBeVisible()
return row
}

View File

@@ -2,6 +2,8 @@ import type { Page } from '@playwright/test'
import type { AdminUrlUtil } from '../../helpers/adminUrlUtil.js'
import { getRowByCellValueAndAssert } from './getRowByCellValueAndAssert.js'
export async function goToListDoc({
page,
cellClass,
@@ -14,12 +16,7 @@ export async function goToListDoc({
urlUtil: AdminUrlUtil
}) {
await page.goto(urlUtil.list)
const row = page
.locator(`.collection-list .table tr`)
.filter({
has: page.locator(`${cellClass}`, { hasText: textToMatch }),
})
.first()
const row = await getRowByCellValueAndAssert({ page, textToMatch, cellClass })
const cellLink = row.locator(`td a`).first()
const linkURL = await cellLink.getAttribute('href')
await page.goto(`${urlUtil.serverURL}${linkURL}`)