feat: show fields inside groups as separate columns in the list view (#7355)

## Description

Group fields are shown as one column, this PR changes this so that the
individual field is now shown separately.

Before change:
<img width="1227" alt="before change"
src="https://github.com/user-attachments/assets/dfae58fd-8ad2-4329-84fd-ed1d4eb20854">

After change:
<img width="1229" alt="after change"
src="https://github.com/user-attachments/assets/d4fd78bb-c474-436e-a0f5-cac4638b91a4">

- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [X] New feature (non-breaking change which adds functionality)

## Checklist:

- [X] I have added tests that prove my fix is effective or that my
feature works
- [X] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation

---------

Co-authored-by: Patrik Kozak <35232443+PatrikKozak@users.noreply.github.com>
This commit is contained in:
Anders Semb Hermansen
2025-05-21 22:25:34 +02:00
committed by GitHub
parent c772a3207c
commit 2a41d3fbb1
19 changed files with 884 additions and 78 deletions

View File

@@ -143,6 +143,16 @@ export const Posts: CollectionConfig = {
},
],
},
{
name: 'group',
type: 'group',
fields: [
{
name: 'nestedTitle',
type: 'text',
},
],
},
{
name: 'relationship',
type: 'relationship',

View File

@@ -11,6 +11,7 @@ import {
exactText,
getRoutes,
initPageConsoleErrorCatch,
openColumnControls,
} from '../../../helpers.js'
import { AdminUrlUtil } from '../../../helpers/adminUrlUtil.js'
import { initPayloadE2ENoConfig } from '../../../helpers/initPayloadE2ENoConfig.js'
@@ -954,6 +955,20 @@ describe('List View', () => {
expect(page.url()).not.toMatch(/columns=/)
})
test('should render field in group as column', async () => {
await createPost({ group: { nestedTitle: 'nested group title 1' } })
await page.goto(postsUrl.list)
await openColumnControls(page)
await page
.locator('.column-selector .column-selector__column', {
hasText: exactText('Group > Nested Title'),
})
.click()
await expect(page.locator('.row-1 .cell-group-nestedTitle')).toHaveText(
'nested group title 1',
)
})
test('should drag to reorder columns and save to preferences', async () => {
await reorderColumns(page, { fromColumn: 'Number', toColumn: 'ID' })
@@ -1261,7 +1276,7 @@ describe('List View', () => {
beforeEach(async () => {
// delete all posts created by the seed
await deleteAllPosts()
await createPost({ number: 1 })
await createPost({ number: 1, group: { nestedTitle: 'nested group title 1' } })
await createPost({ number: 2 })
})
@@ -1283,6 +1298,34 @@ describe('List View', () => {
await expect(page.locator('.row-2 .cell-number')).toHaveText('1')
})
test('should allow sorting by nested field within group in separate column', async () => {
await page.goto(postsUrl.list)
await openColumnControls(page)
await page
.locator('.column-selector .column-selector__column', {
hasText: exactText('Group > Nested Title'),
})
.click()
const upChevron = page.locator('#heading-group-nestedTitle .sort-column__asc')
const downChevron = page.locator('#heading-group-nestedTitle .sort-column__desc')
await upChevron.click()
await page.waitForURL(/sort=group.nestedTitle/)
await expect(page.locator('.row-1 .cell-group-nestedTitle')).toHaveText('<No Nested Title>')
await expect(page.locator('.row-2 .cell-group-nestedTitle')).toHaveText(
'nested group title 1',
)
await downChevron.click()
await page.waitForURL(/sort=-group.nestedTitle/)
await expect(page.locator('.row-1 .cell-group-nestedTitle')).toHaveText(
'nested group title 1',
)
await expect(page.locator('.row-2 .cell-group-nestedTitle')).toHaveText('<No Nested Title>')
})
test('should sort with existing filters', async () => {
await page.goto(postsUrl.list)

View File

@@ -237,6 +237,9 @@ export interface Post {
[k: string]: unknown;
}[]
| null;
group?: {
nestedTitle?: string | null;
};
relationship?: (string | null) | Post;
users?: (string | null) | User;
customCell?: string | null;
@@ -695,6 +698,11 @@ export interface PostsSelect<T extends boolean = true> {
description?: T;
number?: T;
richText?: T;
group?:
| T
| {
nestedTitle?: T;
};
relationship?: T;
users?: T;
customCell?: T;

View File

@@ -380,6 +380,11 @@ export async function switchTab(page: Page, selector: string) {
await expect(page.locator(`${selector}.tabs-field__tab-button--active`)).toBeVisible()
}
export const openColumnControls = async (page: Page) => {
await page.locator('.list-controls__toggle-columns').click()
await expect(page.locator('.list-controls__columns.rah-static--height-auto')).toBeVisible()
}
/**
* Throws an error when browser console error messages (with some exceptions) are thrown, thus resulting
* in the e2e test failing.