fix(ui): properly reflects hook changes in ui (#10268)

Fixes #9882 and #9691

In 2.0, we would accept data coming back from an update operation and
then reflect those changes in UI.

However, in 3.0, we did not do that anymore - meaning you could change a
document with hooks in `beforeChange` or `afterChange`, but then not see
the changes made on the server.

This PR updates the way that `mergeServerFormState` works, and adds a
property to optionally allow values from server form state - which can
then be used in the `onSuccess` form handler which may need to define
new field values.
This commit is contained in:
James Mikrut
2025-01-03 14:55:52 -05:00
committed by GitHub
parent b44aadee65
commit 3ea1d393fd
9 changed files with 117 additions and 44 deletions

View File

@@ -0,0 +1,22 @@
import type { CollectionConfig } from 'payload'
export const BeforeChangeHooks: CollectionConfig = {
slug: 'before-change-hooks',
hooks: {
beforeChange: [
({ data }) => {
data.title = 'hi from hook'
return data
},
],
},
fields: [
{
name: 'title',
label: 'Title',
type: 'text',
required: true,
},
],
}

View File

@@ -8,6 +8,7 @@ import { APIError } from 'payload'
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
import { AfterOperationCollection } from './collections/AfterOperation/index.js'
import { BeforeChangeHooks } from './collections/BeforeChange/index.js'
import { BeforeValidateCollection } from './collections/BeforeValidate/index.js'
import ChainingHooks from './collections/ChainingHooks/index.js'
import ContextHooks from './collections/ContextHooks/index.js'
@@ -25,6 +26,7 @@ export const HooksConfig: Promise<SanitizedConfig> = buildConfigWithDefaults({
},
},
collections: [
BeforeChangeHooks,
BeforeValidateCollection,
AfterOperationCollection,
ContextHooks,

View File

@@ -23,6 +23,7 @@ let payload: PayloadTestSDK<Config>
describe('Hooks', () => {
let url: AdminUrlUtil
let beforeChangeURL: AdminUrlUtil
let page: Page
let serverURL: string
@@ -31,6 +32,7 @@ describe('Hooks', () => {
;({ payload, serverURL } = await initPayloadE2ENoConfig<Config>({ dirname }))
url = new AdminUrlUtil(serverURL, 'before-validate')
beforeChangeURL = new AdminUrlUtil(serverURL, 'before-change-hooks')
const context = await browser.newContext()
page = await context.newPage()
@@ -58,6 +60,18 @@ describe('Hooks', () => {
await expect(page.locator('#field-title')).toHaveValue('reset in beforeValidate')
})
test('should reflect changes made in beforeChange collection hooks within ui after save', async () => {
await page.goto(beforeChangeURL.create)
await page.locator('#field-title').fill('should replace value with before change response')
await saveDocAndAssert(page)
await expect(page.locator('#field-title')).toHaveValue('hi from hook')
await page.locator('#field-title').fill('helllooooooooo')
await saveDocAndAssert(page)
await expect(page.locator('#field-title')).toHaveValue('hi from hook')
})
})
async function clearAllDocs(): Promise<void> {

View File

@@ -11,6 +11,7 @@ export interface Config {
'hooks-users': HooksUserAuthOperations;
};
collections: {
'before-change-hooks': BeforeChangeHook;
'before-validate': BeforeValidate;
afterOperation: AfterOperation;
'context-hooks': ContextHook;
@@ -27,6 +28,7 @@ export interface Config {
};
collectionsJoins: {};
collectionsSelect: {
'before-change-hooks': BeforeChangeHooksSelect<false> | BeforeChangeHooksSelect<true>;
'before-validate': BeforeValidateSelect<false> | BeforeValidateSelect<true>;
afterOperation: AfterOperationSelect<false> | AfterOperationSelect<true>;
'context-hooks': ContextHooksSelect<false> | ContextHooksSelect<true>;
@@ -77,6 +79,16 @@ export interface HooksUserAuthOperations {
password: string;
};
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "before-change-hooks".
*/
export interface BeforeChangeHook {
id: string;
title: string;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "before-validate".
@@ -231,6 +243,10 @@ export interface DataHook {
export interface PayloadLockedDocument {
id: string;
document?:
| ({
relationTo: 'before-change-hooks';
value: string | BeforeChangeHook;
} | null)
| ({
relationTo: 'before-validate';
value: string | BeforeValidate;
@@ -313,6 +329,15 @@ export interface PayloadMigration {
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "before-change-hooks_select".
*/
export interface BeforeChangeHooksSelect<T extends boolean = true> {
title?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "before-validate_select".