fix: allow unnamed group fields to not set a label at all (#12580)

Technically you could already set `label: undefined` and it would be
supported by group fields but the types didn't reflect this.

So now you can create an unnamed group field like this:

```ts
{
      type: 'group',
      fields: [
        {
          type: 'text',
          name: 'insideGroupWithNoLabel',
        },
      ],
    },
```

This will remove the label while still visually grouping the fields.

![image](https://github.com/user-attachments/assets/ecb0b364-9cff-4d71-bf9f-86961915aecd)

---------

Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
This commit is contained in:
Paul
2025-05-29 15:03:39 -07:00
committed by GitHub
parent 71df378fb0
commit d5611953a7
5 changed files with 34 additions and 14 deletions

View File

@@ -37,7 +37,7 @@ export const MyGroupField: Field = {
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
| **`fields`** \* | Array of field types to nest within this Group. |
| **`label`** | Used as a heading in the Admin Panel and to name the generated GraphQL type. Required when name is undefined, defaults to name converted to words. |
| **`label`** | Used as a heading in the Admin Panel and to name the generated GraphQL type. Defaults to the field name, if defined. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](/docs/fields/overview#validation) |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/overview), include its data in the user JWT. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
@@ -113,8 +113,7 @@ export const ExampleCollection: CollectionConfig = {
## Presentational group fields
You can also use the Group field to create a presentational group of fields. This is useful when you want to group fields together visually without affecting the data structure.
The label will be required when a `name` is not provided.
You can also use the Group field to only visually group fields without affecting the data structure. Not defining a label will render just the grouped fields.
```ts
import type { CollectionConfig } from 'payload'

View File

@@ -746,17 +746,6 @@ export type NamedGroupField = {
export type UnnamedGroupField = {
interfaceName?: never
/**
* Can be either:
* - A string, which will be used as the tab's label.
* - An object, where the key is the language code and the value is the label.
*/
label:
| {
[selectedLanguage: string]: string
}
| LabelFunction
| string
localized?: never
} & Omit<GroupBase, 'name' | 'virtual'>

View File

@@ -119,5 +119,26 @@ describe('Group', () => {
const unnamedNestedGroupField = page.locator(unnamedNestedGroupSelector)
await expect(unnamedNestedGroupField).toBeVisible()
})
test('should display with no label when label is undefined', async () => {
await page.goto(url.create)
// Makes sure the fields are rendered
await page.mouse.wheel(0, 2000)
const nolabelGroupSelector = `.field-type.group-field#field-_index-14 .group-field__header`
const nolabelGroupField = page.locator(nolabelGroupSelector)
await expect(nolabelGroupField).toBeHidden()
// Makes sure the fields are rendered
await page.mouse.wheel(0, 2000)
// Children should render even if the group has no label
const nolabelGroupChildSelector = `.field-type.group-field#field-_index-14 #field-insideGroupWithNoLabel`
const nolabelGroupChildField = page.locator(nolabelGroupChildSelector)
await expect(nolabelGroupChildField).toBeVisible()
})
})
})

View File

@@ -314,6 +314,15 @@ const GroupFields: CollectionConfig = {
},
],
},
{
type: 'group',
fields: [
{
type: 'text',
name: 'insideGroupWithNoLabel',
},
],
},
{
type: 'group',
label: 'Deeply nested group',

View File

@@ -1082,6 +1082,7 @@ export interface GroupField {
| null;
};
insideUnnamedGroup?: string | null;
insideGroupWithNoLabel?: string | null;
deeplyNestedGroup?: {
insideNestedUnnamedGroup?: string | null;
};
@@ -2685,6 +2686,7 @@ export interface GroupFieldsSelect<T extends boolean = true> {
email?: T;
};
insideUnnamedGroup?: T;
insideGroupWithNoLabel?: T;
deeplyNestedGroup?:
| T
| {