diff --git a/packages/payload/src/fields/hooks/beforeDuplicate/promise.ts b/packages/payload/src/fields/hooks/beforeDuplicate/promise.ts index 9a1b93f822..9fcd3b23f7 100644 --- a/packages/payload/src/fields/hooks/beforeDuplicate/promise.ts +++ b/packages/payload/src/fields/hooks/beforeDuplicate/promise.ts @@ -40,6 +40,24 @@ export const promise = async ({ parentSchemaPath, }) + // Handle unnamed tabs + if (field.type === 'tab' && !tabHasName(field)) { + await traverseFields({ + id, + collection, + context, + doc, + fields: field.fields, + overrideAccess, + path: fieldPath, + req, + schemaPath: fieldSchemaPath, + siblingDoc, + }) + + return + } + if (fieldAffectsData(field)) { let fieldData = siblingDoc?.[field.name] const fieldIsLocalized = field.localized && localization @@ -207,40 +225,25 @@ export const promise = async ({ switch (field.type) { case 'tab': case 'group': { - if (field.type === 'tab' && !tabHasName(field)) { - await traverseFields({ - id, - collection, - context, - doc, - fields: field.fields, - overrideAccess, - path: fieldPath, - req, - schemaPath: fieldSchemaPath, - siblingDoc, - }) - } else { - if (typeof siblingDoc[field.name] !== 'object') { - siblingDoc[field.name] = {} - } - - const groupDoc = siblingDoc[field.name] as Record - - await traverseFields({ - id, - collection, - context, - doc, - fields: field.fields, - overrideAccess, - path: fieldPath, - req, - schemaPath: fieldSchemaPath, - siblingDoc: groupDoc as JsonObject, - }) + if (typeof siblingDoc[field.name] !== 'object') { + siblingDoc[field.name] = {} } + const groupDoc = siblingDoc[field.name] as Record + + await traverseFields({ + id, + collection, + context, + doc, + fields: field.fields, + overrideAccess, + path: fieldPath, + req, + schemaPath: fieldSchemaPath, + siblingDoc: groupDoc as JsonObject, + }) + break } diff --git a/test/localization/config.ts b/test/localization/config.ts index aedd840e2c..b83eddc903 100644 --- a/test/localization/config.ts +++ b/test/localization/config.ts @@ -123,44 +123,77 @@ export default buildConfigWithDefaults({ type: 'text', }, { - name: 'layout', - blocks: [ + type: 'tabs', + tabs: [ { + label: 'Main Nav', fields: [ { - name: 'text', - type: 'text', - }, - { - name: 'nestedArray', - type: 'array', + name: 'nav', + type: 'group', fields: [ { - name: 'text', - type: 'text', + name: 'layout', + blocks: [ + { + fields: [ + { + name: 'text', + type: 'text', + }, + { + name: 'nestedArray', + type: 'array', + fields: [ + { + name: 'text', + type: 'text', + }, + { + name: 'l2', + type: 'array', + fields: [ + { + name: 'l3', + type: 'array', + fields: [ + { + name: 'l4', + type: 'array', + fields: [ + { + name: 'superNestedText', + type: 'text', + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + slug: 'text', + }, + { + fields: [ + { + name: 'number', + type: 'number', + }, + ], + slug: 'number', + }, + ], + localized: true, + required: true, + type: 'blocks', }, ], }, ], - slug: 'text', }, - { - fields: [ - { - name: 'number', - type: 'number', - }, - ], - slug: 'number', - }, - ], - localized: true, - required: true, - type: 'blocks', - }, - { - type: 'tabs', - tabs: [ { name: 'myTab', fields: [ diff --git a/test/localization/e2e.spec.ts b/test/localization/e2e.spec.ts index 7c4e81eae3..475ebea817 100644 --- a/test/localization/e2e.spec.ts +++ b/test/localization/e2e.spec.ts @@ -205,10 +205,10 @@ describe('Localization', () => { await page.goto(urlWithRequiredLocalizedFields.create) await changeLocale(page, defaultLocale) await page.locator('#field-title').fill(englishTitle) - await page.locator('#field-layout .blocks-field__drawer-toggler').click() + await page.locator('#field-nav__layout .blocks-field__drawer-toggler').click() await page.locator('button[title="Text"]').click() - await page.fill('#field-layout__0__text', 'test') - await expect(page.locator('#field-layout__0__text')).toHaveValue('test') + await page.fill('#field-nav__layout__0__text', 'test') + await expect(page.locator('#field-nav__layout__0__text')).toHaveValue('test') await saveDocAndAssert(page) const originalID = await page.locator('.id-label').innerText() await openDocControls(page) @@ -256,6 +256,10 @@ describe('Localization', () => { async function fillValues(data: Partial) { const { description: descVal, title: titleVal } = data - if (titleVal) {await page.locator('#field-title').fill(titleVal)} - if (descVal) {await page.locator('#field-description').fill(descVal)} + if (titleVal) { + await page.locator('#field-title').fill(titleVal) + } + if (descVal) { + await page.locator('#field-description').fill(descVal) + } } diff --git a/test/localization/int.spec.ts b/test/localization/int.spec.ts index d1e81de5e0..4cf5d4b24f 100644 --- a/test/localization/int.spec.ts +++ b/test/localization/int.spec.ts @@ -709,12 +709,14 @@ describe('Localization', () => { const newDoc = await payload.create({ collection: withRequiredLocalizedFields, data: { - layout: [ - { - blockType: 'text', - text: 'laiwejfilwaje', - }, - ], + nav: { + layout: [ + { + blockType: 'text', + text: 'laiwejfilwaje', + }, + ], + }, title: 'hello', }, }) @@ -723,12 +725,14 @@ describe('Localization', () => { id: newDoc.id, collection: withRequiredLocalizedFields, data: { - layout: [ - { - blockType: 'number', - number: 12, - }, - ], + nav: { + layout: [ + { + blockType: 'number', + number: 12, + }, + ], + }, title: 'en espanol, big bird', }, locale: spanishLocale, @@ -742,7 +746,7 @@ describe('Localization', () => { }, }) - expect(updatedDoc.layout[0].blockType).toStrictEqual('text') + expect(updatedDoc.nav.layout[0].blockType).toStrictEqual('text') const spanishDoc = await payload.findByID({ id: newDoc.id, @@ -750,7 +754,7 @@ describe('Localization', () => { locale: spanishLocale, }) - expect(spanishDoc.layout[0].blockType).toStrictEqual('number') + expect(spanishDoc.nav.layout[0].blockType).toStrictEqual('number') }) }) @@ -1126,26 +1130,56 @@ describe('Localization', () => { // - and this needs to be done recursively for all block / array fields // 2. make sure localized arrays / blocks work inside of localized groups / tabs // - this is covered with myTab.group.nestedArray2 + // 3. the field schema for `nav` is within an unnamed tab, which tests that we + // properly recursively loop through all field structures / types const englishText = 'english' const spanishText = 'spanish' const doc = await payload.create({ collection: withRequiredLocalizedFields, data: { - layout: [ - { - blockType: 'text', - text: englishText, - nestedArray: [ - { - text: 'hello', - }, - { - text: 'goodbye', - }, - ], - }, - ], + nav: { + layout: [ + { + blockType: 'text', + text: englishText, + nestedArray: [ + { + text: 'hello', + l2: [ + { + l3: [ + { + l4: [ + { + superNestedText: 'hello', + }, + ], + }, + ], + }, + ], + }, + { + text: 'goodbye', + l2: [ + { + l3: [ + { + l4: [ + { + superNestedText: 'goodbye', + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, myTab: { text: 'hello', group: { @@ -1169,20 +1203,48 @@ describe('Localization', () => { id: doc.id, collection: withRequiredLocalizedFields, data: { - layout: [ - { - blockType: 'text', - text: spanishText, - nestedArray: [ - { - text: 'hola', - }, - { - text: 'adios', - }, - ], - }, - ], + nav: { + layout: [ + { + blockType: 'text', + text: spanishText, + nestedArray: [ + { + text: 'hola', + l2: [ + { + l3: [ + { + l4: [ + { + superNestedText: 'hola', + }, + ], + }, + ], + }, + ], + }, + { + text: 'adios', + l2: [ + { + l3: [ + { + l4: [ + { + superNestedText: 'adios', + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, title: 'hello', myTab: { text: 'hola', @@ -1215,10 +1277,10 @@ describe('Localization', () => { }) // check fields - expect(result.layout[0].text).toStrictEqual(englishText) + expect(result.nav.layout[0].text).toStrictEqual(englishText) - expect(allLocales.layout.en[0].text).toStrictEqual(englishText) - expect(allLocales.layout.es[0].text).toStrictEqual(spanishText) + expect(allLocales.nav.layout.en[0].text).toStrictEqual(englishText) + expect(allLocales.nav.layout.es[0].text).toStrictEqual(spanishText) expect(allLocales.myTab.group.en.nestedText).toStrictEqual('hello') expect(allLocales.myTab.group.en.nestedArray2[0].nestedText).toStrictEqual('hello') diff --git a/test/localization/payload-types.ts b/test/localization/payload-types.ts index 56d34ca2c5..fe58cfe366 100644 --- a/test/localization/payload-types.ts +++ b/test/localization/payload-types.ts @@ -26,6 +26,7 @@ export interface Config { tabs: Tab; 'localized-sort': LocalizedSort; 'blocks-same-name': BlocksSameName; + 'localized-within-localized': LocalizedWithinLocalized; 'payload-preferences': PayloadPreference; 'payload-migrations': PayloadMigration; }; @@ -74,6 +75,14 @@ export interface BlocksField { blockType: 'textBlock'; }[] | null; + array?: + | { + link?: { + label?: string | null; + }; + id?: string | null; + }[] + | null; id?: string | null; blockName?: string | null; blockType: 'blockInsideBlock'; @@ -93,6 +102,9 @@ export interface NestedArray { blocksWithinArray?: | { relationWithinBlock?: (string | null) | LocalizedPost; + myGroup?: { + text?: string | null; + }; id?: string | null; blockName?: string | null; blockType: 'someBlock'; @@ -109,6 +121,7 @@ export interface NestedArray { | null; updatedAt: string; createdAt: string; + _status?: ('draft' | 'published') | null; } /** * This interface was referenced by `Config`'s JSON-Schema @@ -195,20 +208,56 @@ export interface ArrayField { export interface LocalizedRequired { id: string; title: string; - layout: ( - | { - text?: string | null; - id?: string | null; - blockName?: string | null; - blockType: 'text'; - } - | { - number?: number | null; - id?: string | null; - blockName?: string | null; - blockType: 'number'; - } - )[]; + nav: { + layout: ( + | { + text?: string | null; + nestedArray?: + | { + text?: string | null; + l2?: + | { + l3?: + | { + l4?: + | { + superNestedText?: string | null; + id?: string | null; + }[] + | null; + id?: string | null; + }[] + | null; + id?: string | null; + }[] + | null; + id?: string | null; + }[] + | null; + id?: string | null; + blockName?: string | null; + blockType: 'text'; + } + | { + number?: number | null; + id?: string | null; + blockName?: string | null; + blockType: 'number'; + } + )[]; + }; + myTab?: { + text?: string | null; + group?: { + nestedArray2?: + | { + nestedText?: string | null; + id?: string | null; + }[] + | null; + nestedText?: string | null; + }; + }; updatedAt: string; createdAt: string; } @@ -413,6 +462,35 @@ export interface BlocksSameName { updatedAt: string; createdAt: string; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "localized-within-localized". + */ +export interface LocalizedWithinLocalized { + id: string; + myTab?: { + shouldNotBeLocalized?: string | null; + }; + myArray?: + | { + shouldNotBeLocalized?: string | null; + id?: string | null; + }[] + | null; + myBlocks?: + | { + shouldNotBeLocalized?: string | null; + id?: string | null; + blockName?: string | null; + blockType: 'myBlock'; + }[] + | null; + myGroup?: { + shouldNotBeLocalized?: string | null; + }; + updatedAt: string; + createdAt: string; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-preferences".