diff --git a/packages/next/src/views/Document/index.tsx b/packages/next/src/views/Document/index.tsx index a88ba63cfde..d0d4742c019 100644 --- a/packages/next/src/views/Document/index.tsx +++ b/packages/next/src/views/Document/index.tsx @@ -49,7 +49,7 @@ export const renderDocument = async ({ }> => { const { collectionConfig, - docID: id, + docID: idFromArgs, globalConfig, locale, permissions, @@ -72,7 +72,7 @@ export const renderDocument = async ({ const segments = Array.isArray(params?.segments) ? params.segments : [] const collectionSlug = collectionConfig?.slug || undefined const globalSlug = globalConfig?.slug || undefined - const isEditing = getIsEditing({ id, collectionSlug, globalSlug }) + let isEditing = getIsEditing({ id: idFromArgs, collectionSlug, globalSlug }) let RootViewOverride: PayloadComponent let CustomView: ViewFromConfig @@ -82,10 +82,10 @@ export const renderDocument = async ({ let apiURL: string // Fetch the doc required for the view - const doc = + let doc = initialData || (await getDocumentData({ - id, + id: idFromArgs, collectionSlug, globalSlug, locale, @@ -104,7 +104,7 @@ export const renderDocument = async ({ ] = await Promise.all([ // Get document preferences getDocPreferences({ - id, + id: idFromArgs, collectionSlug, globalSlug, payload, @@ -113,7 +113,7 @@ export const renderDocument = async ({ // Get permissions getDocumentPermissions({ - id, + id: idFromArgs, collectionConfig, data: doc, globalConfig, @@ -122,7 +122,7 @@ export const renderDocument = async ({ // Fetch document lock state getIsLocked({ - id, + id: idFromArgs, collectionConfig, globalConfig, isEditing, @@ -135,7 +135,7 @@ export const renderDocument = async ({ { state: formState }, ] = await Promise.all([ getVersions({ - id, + id: idFromArgs, collectionConfig, docPermissions, globalConfig, @@ -144,7 +144,7 @@ export const renderDocument = async ({ user, }), buildFormState({ - id, + id: idFromArgs, collectionSlug, data: doc, docPermissions, @@ -152,7 +152,7 @@ export const renderDocument = async ({ fallbackLocale: false, globalSlug, locale: locale?.code, - operation: (collectionSlug && id) || globalSlug ? 'update' : 'create', + operation: (collectionSlug && idFromArgs) || globalSlug ? 'update' : 'create', renderAllFields: true, req, schemaPath: collectionSlug || globalSlug, @@ -187,7 +187,7 @@ export const renderDocument = async ({ const apiQueryParams = `?${params.toString()}` - apiURL = `${serverURL}${apiRoute}/${collectionSlug}/${id}${apiQueryParams}` + apiURL = `${serverURL}${apiRoute}/${collectionSlug}/${idFromArgs}${apiQueryParams}` RootViewOverride = collectionConfig?.admin?.components?.views?.edit?.root && @@ -274,8 +274,10 @@ export const renderDocument = async ({ const validateDraftData = collectionConfig?.versions?.drafts && collectionConfig?.versions?.drafts?.validate - if (shouldAutosave && !validateDraftData && !id && collectionSlug) { - const doc = await payload.create({ + let id = idFromArgs + + if (shouldAutosave && !validateDraftData && !idFromArgs && collectionSlug) { + doc = await payload.create({ collection: collectionSlug, data: initialData || {}, depth: 0, @@ -287,12 +289,18 @@ export const renderDocument = async ({ }) if (doc?.id) { - const redirectURL = formatAdminURL({ - adminRoute, - path: `/collections/${collectionSlug}/${doc.id}`, - serverURL, - }) - redirect(redirectURL) + id = doc.id + isEditing = getIsEditing({ id: doc.id, collectionSlug, globalSlug }) + + if (!drawerSlug) { + const redirectURL = formatAdminURL({ + adminRoute, + path: `/collections/${collectionSlug}/${doc.id}`, + serverURL, + }) + + redirect(redirectURL) + } } else { throw new Error('not-found') } diff --git a/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx b/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx index 8dc69f2b305..63b7e92a7bb 100644 --- a/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx +++ b/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx @@ -1,7 +1,7 @@ 'use client' import { useModal } from '@faceless-ui/modal' -import React, { useCallback, useEffect, useState } from 'react' +import React, { useCallback, useEffect, useRef, useState } from 'react' import { toast } from 'sonner' import type { DocumentDrawerProps } from './types.js' @@ -45,6 +45,7 @@ export const DocumentDrawerContent: React.FC = ({ const [DocumentView, setDocumentView] = useState(undefined) const [isLoading, setIsLoading] = useState(true) + const hasRenderedDocument = useRef(false) const getDocumentView = useCallback( (docID?: number | string) => { @@ -142,8 +143,9 @@ export const DocumentDrawerContent: React.FC = ({ }, [getDocumentView]) useEffect(() => { - if (!DocumentView) { + if (!DocumentView && !hasRenderedDocument.current) { getDocumentView(existingDocID) + hasRenderedDocument.current = true } }, [DocumentView, getDocumentView, existingDocID]) diff --git a/packages/ui/src/providers/ServerFunctions/index.tsx b/packages/ui/src/providers/ServerFunctions/index.tsx index 144c363a263..7309fd1cb79 100644 --- a/packages/ui/src/providers/ServerFunctions/index.tsx +++ b/packages/ui/src/providers/ServerFunctions/index.tsx @@ -32,7 +32,7 @@ type RenderDocument = (args: { redirectAfterDelete?: boolean redirectAfterDuplicate?: boolean signal?: AbortSignal -}) => Promise<{ docID: string; Document: React.ReactNode }> +}) => Promise<{ data: Data; Document: React.ReactNode }> type GetDocumentSlots = (args: { collectionSlug: string @@ -129,16 +129,12 @@ export const ServerFunctionsProvider: React.FC<{ const { signal: remoteSignal, ...rest } = args || {} try { - if (!remoteSignal?.aborted) { - const result = (await serverFunction({ - name: 'render-document', - args: { fallbackLocale: false, ...rest }, - })) as { docID: string; Document: React.ReactNode } + const result = (await serverFunction({ + name: 'render-document', + args: { fallbackLocale: false, ...rest }, + })) as { data: Data; Document: React.ReactNode } - if (!remoteSignal?.aborted) { - return result - } - } + return result } catch (_err) { console.error(_err) // eslint-disable-line no-console } diff --git a/test/fields-relationship/e2e.spec.ts b/test/fields-relationship/e2e.spec.ts index 2673ce42e09..c34cd420717 100644 --- a/test/fields-relationship/e2e.spec.ts +++ b/test/fields-relationship/e2e.spec.ts @@ -179,10 +179,9 @@ describe('fields - relationship', () => { await expect(options).toHaveCount(2) // two docs await options.nth(0).click() await expect(field).toContainText(relationOneDoc.id) - await saveDocAndAssert(page) - await wait(200) - await trackNetworkRequests(page, `/api/${relationOneSlug}`, { - beforePoll: async () => await page.reload(), + await trackNetworkRequests(page, `/api/${relationOneSlug}`, async () => { + await saveDocAndAssert(page) + await wait(200) }) }) diff --git a/test/helpers/e2e/trackNetworkRequests.ts b/test/helpers/e2e/trackNetworkRequests.ts index e13399ff993..64c684b2c49 100644 --- a/test/helpers/e2e/trackNetworkRequests.ts +++ b/test/helpers/e2e/trackNetworkRequests.ts @@ -8,6 +8,7 @@ import { expect } from '@playwright/test' export const trackNetworkRequests = async ( page: Page, url: string, + action: () => Promise, options?: { allowedNumberOfRequests?: number beforePoll?: () => Promise | void @@ -26,6 +27,8 @@ export const trackNetworkRequests = async ( } }) + await action() + if (typeof beforePoll === 'function') { await beforePoll() } diff --git a/test/versions/e2e.spec.ts b/test/versions/e2e.spec.ts index fdc54f1a58b..ef300e4db19 100644 --- a/test/versions/e2e.spec.ts +++ b/test/versions/e2e.spec.ts @@ -25,6 +25,7 @@ import type { BrowserContext, Page } from '@playwright/test' import { expect, test } from '@playwright/test' +import { navigateToDoc } from 'helpers/e2e/navigateToDoc.js' import path from 'path' import { wait } from 'payload/shared' import { fileURLToPath } from 'url' @@ -43,6 +44,7 @@ import { throttleTest, } from '../helpers.js' import { AdminUrlUtil } from '../helpers/adminUrlUtil.js' +import { trackNetworkRequests } from '../helpers/e2e/trackNetworkRequests.js' import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js' import { reInitializeDB } from '../helpers/reInitializeDB.js' import { waitForAutoSaveToRunAndComplete } from '../helpers/waitForAutoSaveToRunAndComplete.js' @@ -394,6 +396,42 @@ describe('versions', () => { expect(page.url()).toMatch(/\/versions$/) }) + test('collection - should autosave', async () => { + await page.goto(autosaveURL.create) + await page.locator('#field-title').fill('autosave title') + await waitForAutoSaveToRunAndComplete(page) + await expect(page.locator('#field-title')).toHaveValue('autosave title') + + const { id: postID } = await payload.create({ + collection: postCollectionSlug, + data: { + title: 'post title', + description: 'post description', + }, + }) + + await page.goto(postURL.edit(postID)) + + await trackNetworkRequests( + page, + `${serverURL}/admin/collections/${postCollectionSlug}/${postID}`, + async () => { + await page + .locator( + '#field-relationToAutosaves.field-type.relationship .relationship-add-new__add-button.doc-drawer__toggler', + ) + .click() + }, + { + allowedNumberOfRequests: 1, + }, + ) + + const drawer = page.locator('[id^=doc-drawer_autosave-posts_1_]') + await expect(drawer).toBeVisible() + await expect(drawer.locator('.id-label')).toBeVisible() + }) + test('global - should autosave', async () => { const url = new AdminUrlUtil(serverURL, autoSaveGlobalSlug) await page.goto(url.global(autoSaveGlobalSlug)) diff --git a/tsconfig.json b/tsconfig.json index 314fd332c39..ac5eff74319 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -37,7 +37,7 @@ ], "paths": { "@payload-config": [ - "./test/_community/config.ts" + "./test/versions/config.ts" ], "@payloadcms/live-preview": [ "./packages/live-preview/src"