fix: localized tabs with empty data and an array field inside lead to crash in afterChange (#10410)

Previously, this config:
```ts
import type { CollectionConfig } from 'payload'

export const tabSlug = 'tabs'

export const Tab: CollectionConfig = {
  slug: tabSlug,
  fields: [
    {
      type: 'tabs',
      tabs: [
        {
          name: 'tabLocalized',
          localized: true,
          fields: [
            {
              name: 'title',
              type: 'text',
            },
            {
              name: 'array',
              type: 'array',
              fields: [
                {
                  name: 'title',
                  type: 'text',
                },
              ],
            },
          ],
        },
      ],
    },
  ],
}

```
This call
```ts
const result = await payload.create({
  collection: tabSlug,
  locale: englishLocale,
  data: {
    tabLocalized: {},
  },
})
```

Led to the following crash with the MongoDB adapter
<img width="741" alt="image"
src="https://github.com/user-attachments/assets/8d1d37de-a685-4a30-bd37-58af164108a2"
/>

This is due to how Mongoose, apparently just ignores the `minimize:
false` configuration
a83a430a3a/packages/db-mongodb/src/models/buildSchema.ts (L571)
and we, instead of `tabLocalized: { en: { } }` receive just
`tabLocalized: {}`.

This isn't an issue with group fields because we have fallback for them

a83a430a3a/packages/payload/src/fields/hooks/afterChange/promise.ts (L203)

This PR adds the same for tabs.
This commit is contained in:
Sasha
2025-01-11 00:37:23 +02:00
committed by GitHub
parent 04733f0db1
commit 1af7d8745c
4 changed files with 83 additions and 6 deletions

View File

@@ -165,7 +165,6 @@ export const promise = async ({
}
case 'collapsible':
case 'row': {
await traverseFields({
collection,
@@ -253,9 +252,9 @@ export const promise = async ({
let tabPreviousSiblingDoc = siblingDoc
if (tabHasName(field)) {
tabSiblingData = siblingData[field.name] as JsonObject
tabSiblingDoc = siblingDoc[field.name] as JsonObject
tabPreviousSiblingDoc = previousDoc[field.name] as JsonObject
tabSiblingData = (siblingData[field.name] as JsonObject) ?? {}
tabSiblingDoc = (siblingDoc[field.name] as JsonObject) ?? {}
tabPreviousSiblingDoc = (previousDoc[field.name] as JsonObject) ?? {}
}
await traverseFields({

View File

@@ -1,4 +1,4 @@
import type { CollectionConfig } from 'payload/types'
import type { CollectionConfig } from 'payload'
export const tabSlug = 'tabs'
@@ -16,6 +16,16 @@ export const Tab: CollectionConfig = {
name: 'title',
type: 'text',
},
{
name: 'array',
type: 'array',
fields: [
{
name: 'title',
type: 'text',
},
],
},
],
},
{

View File

@@ -1646,7 +1646,19 @@ describe('Localization', () => {
expect(all.groupLocalizedRow.es.text).toBe('hola world or something')
})
it('should properly create/update/read localized tab field', async () => {
it('should not crash on empty localized tab', async () => {
const result = await payload.create({
collection: tabSlug,
locale: englishLocale,
data: {
tabLocalized: {},
},
})
expect(result).toBeTruthy()
})
it('should properly create/update/read array field inside localized tab field', async () => {
const result = await payload.create({
collection: tabSlug,
locale: englishLocale,
@@ -1686,6 +1698,50 @@ describe('Localization', () => {
expect(docEs.tabLocalized.title).toBe('hello es')
})
it('should properly create/update/read localized tab field', async () => {
const result = await payload.create({
collection: tabSlug,
locale: englishLocale,
data: {
tabLocalized: {
array: [
{
title: 'hello en',
},
],
},
},
})
expect(result.tabLocalized.array[0].title).toBe('hello en')
await payload.update({
collection: tabSlug,
locale: spanishLocale,
id: result.id,
data: {
tabLocalized: {
array: [{ title: 'hello es' }],
},
},
})
const docEn = await payload.findByID({
collection: tabSlug,
locale: englishLocale,
id: result.id,
})
const docEs = await payload.findByID({
collection: tabSlug,
locale: spanishLocale,
id: result.id,
})
expect(docEn.tabLocalized.array[0].title).toBe('hello en')
expect(docEs.tabLocalized.array[0].title).toBe('hello es')
})
it('should properly create/update/read localized field inside of tab', async () => {
const result = await payload.create({
collection: tabSlug,

View File

@@ -522,6 +522,12 @@ export interface Tab {
id: string;
tabLocalized?: {
title?: string | null;
array?:
| {
title?: string | null;
id?: string | null;
}[]
| null;
};
tab?: {
title?: string | null;
@@ -1119,6 +1125,12 @@ export interface TabsSelect<T extends boolean = true> {
| T
| {
title?: T;
array?:
| T
| {
title?: T;
id?: T;
};
};
tab?:
| T