diff --git a/.vscode/launch.json b/.vscode/launch.json index 032c60c512..3ba165a448 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -40,6 +40,13 @@ "request": "launch", "type": "node-terminal" }, + { + "command": "pnpm run dev localization", + "cwd": "${workspaceFolder}", + "name": "Run Dev Localization", + "request": "launch", + "type": "node-terminal" + }, { "command": "PAYLOAD_BUNDLER=vite pnpm run dev fields", "cwd": "${workspaceFolder}", diff --git a/packages/payload/src/admin/components/elements/DuplicateDocument/index.tsx b/packages/payload/src/admin/components/elements/DuplicateDocument/index.tsx index 612e6012c2..900e75d41b 100644 --- a/packages/payload/src/admin/components/elements/DuplicateDocument/index.tsx +++ b/packages/payload/src/admin/components/elements/DuplicateDocument/index.tsx @@ -46,8 +46,8 @@ const Duplicate: React.FC = ({ id, collection, slug }) => { const saveDocument = async ({ id, - locale = '', duplicateID = '', + locale = '', }): Promise => { const response = await requests.get(`${serverURL}${api}/${slug}/${id}`, { headers: { @@ -56,6 +56,7 @@ const Duplicate: React.FC = ({ id, collection, slug }) => { params: { depth: 0, draft: true, + 'fallback-locale': 'none', locale, }, }) @@ -75,7 +76,7 @@ const Duplicate: React.FC = ({ id, collection, slug }) => { } const result = await requests[duplicateID ? 'patch' : 'post']( - `${serverURL}${api}/${slug}/${duplicateID}?locale=${locale}`, + `${serverURL}${api}/${slug}/${duplicateID}?locale=${locale}&fallback-locale=none`, { body: JSON.stringify(data), headers: { @@ -100,7 +101,7 @@ const Duplicate: React.FC = ({ id, collection, slug }) => { await localization.localeCodes.reduce(async (priorLocalePatch, locale) => { await priorLocalePatch if (abort) return - duplicateID = await saveDocument({ locale, id, duplicateID }) + duplicateID = await saveDocument({ id, duplicateID, locale }) if (!duplicateID) { abort = true } diff --git a/test/localization/config.ts b/test/localization/config.ts index 4b50e9ff9e..ff72a9a9d2 100644 --- a/test/localization/config.ts +++ b/test/localization/config.ts @@ -83,6 +83,11 @@ export default buildConfigWithDefaults({ name: 'description', type: 'text', }, + { + name: 'localizedCheckbox', + type: 'checkbox', + localized: true, + }, ], }, ArrayCollection, diff --git a/test/localization/e2e.spec.ts b/test/localization/e2e.spec.ts index 36eaf8d4d7..6bcdc313f7 100644 --- a/test/localization/e2e.spec.ts +++ b/test/localization/e2e.spec.ts @@ -5,6 +5,7 @@ import { expect, test } from '@playwright/test' import type { LocalizedPost } from './payload-types' import payload from '../../packages/payload/src' +import wait from '../../packages/payload/src/utilities/wait' import { changeLocale, openDocControls, saveDocAndAssert } from '../helpers' import { AdminUrlUtil } from '../helpers/adminUrlUtil' import { initPayloadTest } from '../helpers/configHelpers' @@ -131,7 +132,9 @@ describe('Localization', () => { collection: localizedPostsSlug, data: { title: englishTitle, + localizedCheckbox: true, }, + locale: defaultLocale, }) const id = localizedPost.id.toString() @@ -147,11 +150,46 @@ describe('Localization', () => { await page.goto(url.edit(id)) await openDocControls(page) + + // duplicate document await page.locator('#action-duplicate').click() await expect(page.locator('.Toastify')).toContainText('successfully') + + // check fields await expect(page.locator('#field-title')).toHaveValue(englishTitle) await changeLocale(page, spanishLocale) await expect(page.locator('#field-title')).toHaveValue(spanishTitle) + + // click checkbox manually + await page.locator('#field-localizedCheckbox').click() + await expect(page.locator('#field-localizedCheckbox')).not.toBeChecked() + }) + + test('should duplicate localized checkbox correctly', async () => { + await page.goto(url.create) + await changeLocale(page, defaultLocale) + await fillValues({ title: englishTitle, description }) + await page.locator('#field-localizedCheckbox').click() + + await page.locator('#action-save').click() + // wait for navigation to update route + await wait(500) + + // ensure spanish is not checked + await changeLocale(page, spanishLocale) + await expect(page.locator('#field-localizedCheckbox')).not.toBeChecked() + + // duplicate doc + await changeLocale(page, defaultLocale) + await openDocControls(page) + await page.locator('#action-duplicate').click() + + // wait for navigation to update route + await wait(500) + + // finally change locale to spanish + await changeLocale(page, spanishLocale) + await expect(page.locator('#field-localizedCheckbox')).not.toBeChecked() }) }) }) diff --git a/test/localization/payload-types.ts b/test/localization/payload-types.ts index d75bf27a3f..6abfdb95ad 100644 --- a/test/localization/payload-types.ts +++ b/test/localization/payload-types.ts @@ -1,4 +1,5 @@ /* tslint:disable */ +/* eslint-disable */ /** * This file was automatically generated by Payload. * DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config, @@ -14,6 +15,8 @@ export interface Config { 'with-localized-relationship': WithLocalizedRelationship 'relationship-localized': RelationshipLocalized dummy: Dummy + 'payload-preferences': PayloadPreference + 'payload-migrations': PayloadMigration } globals: { 'global-array': GlobalArray @@ -21,136 +24,163 @@ export interface Config { } export interface User { id: string - relation?: string | LocalizedPost - email?: string - resetPasswordToken?: string - resetPasswordExpiration?: string - loginAttempts?: number - lockUntil?: string - createdAt: string + relation?: (string | null) | LocalizedPost updatedAt: string - password?: string + createdAt: string + email: string + resetPasswordToken?: string | null + resetPasswordExpiration?: string | null + salt?: string | null + hash?: string | null + loginAttempts?: number | null + lockUntil?: string | null + password: string | null } export interface LocalizedPost { id: string - title?: string - description?: string - createdAt: string + title?: string | null + description?: string | null + localizedCheckbox?: boolean | null updatedAt: string + createdAt: string } export interface ArrayField { id: string - items?: { - text: string - id?: string - }[] - createdAt: string + items?: + | { + text: string + id?: string | null + }[] + | null updatedAt: string + createdAt: string } export interface LocalizedRequired { id: string title: string layout: ( | { - text?: string - id?: string - blockName?: string + text?: string | null + id?: string | null + blockName?: string | null blockType: 'text' } | { - number?: number - id?: string - blockName?: string + number?: number | null + id?: string | null + blockName?: string | null blockType: 'number' } )[] - createdAt: string updatedAt: string + createdAt: string } export interface WithLocalizedRelationship { id: string - localizedRelationship?: string | LocalizedPost - localizedRelationHasManyField?: string[] | LocalizedPost[] + localizedRelationship?: (string | null) | LocalizedPost + localizedRelationHasManyField?: (string | LocalizedPost)[] | null localizedRelationMultiRelationTo?: - | { - value: string | LocalizedPost + | ({ relationTo: 'localized-posts' - } - | { - value: string | Dummy + value: string | LocalizedPost + } | null) + | ({ relationTo: 'dummy' - } + value: string | Dummy + } | null) localizedRelationMultiRelationToHasMany?: | ( | { - value: string relationTo: 'localized-posts' + value: string | LocalizedPost } | { - value: string relationTo: 'dummy' + value: string | Dummy } )[] - | ( - | { - value: LocalizedPost - relationTo: 'localized-posts' - } - | { - value: Dummy - relationTo: 'dummy' - } - )[] - createdAt: string + | null updatedAt: string + createdAt: string } export interface Dummy { id: string - name?: string - createdAt: string + name?: string | null updatedAt: string + createdAt: string } export interface RelationshipLocalized { id: string - relationship?: string | LocalizedPost - relationshipHasMany?: string[] | LocalizedPost[] + relationship?: (string | null) | LocalizedPost + relationshipHasMany?: (string | LocalizedPost)[] | null relationMultiRelationTo?: - | { - value: string | LocalizedPost + | ({ relationTo: 'localized-posts' - } - | { - value: string | Dummy + value: string | LocalizedPost + } | null) + | ({ relationTo: 'dummy' - } + value: string | Dummy + } | null) relationMultiRelationToHasMany?: | ( | { - value: string relationTo: 'localized-posts' + value: string | LocalizedPost } | { - value: string relationTo: 'dummy' + value: string | Dummy } )[] - | ( - | { - value: LocalizedPost - relationTo: 'localized-posts' - } - | { - value: Dummy - relationTo: 'dummy' - } - )[] - createdAt: string + | null + arrayField?: + | { + nestedRelation?: (string | null) | LocalizedPost + id?: string | null + }[] + | null updatedAt: string + createdAt: string +} +export interface PayloadPreference { + id: string + user: { + relationTo: 'users' + value: string | User + } + key?: string | null + value?: + | { + [k: string]: unknown + } + | unknown[] + | string + | number + | boolean + | null + updatedAt: string + createdAt: string +} +export interface PayloadMigration { + id: string + name?: string | null + batch?: number | null + updatedAt: string + createdAt: string } export interface GlobalArray { id: string - array?: { - text?: string - id?: string - }[] + array?: + | { + text?: string | null + id?: string | null + }[] + | null + updatedAt?: string | null + createdAt?: string | null +} + +declare module 'payload' { + export interface GeneratedTypes extends Config {} }