chore: duplicates prev value PRs from v2 (#7414)
Updates V3 with V2 PR's - previousVersion type https://github.com/payloadcms/payload/pull/6805 - tests from https://github.com/payloadcms/payload/pull/6805
This commit is contained in:
@@ -175,20 +175,22 @@ export type Labels = {
|
||||
singular: LabelFunction | LabelStatic
|
||||
}
|
||||
|
||||
export type BaseValidateOptions<TData, TSiblingData> = {
|
||||
export type BaseValidateOptions<TData, TSiblingData, TValue> = {
|
||||
data: Partial<TData>
|
||||
id?: number | string
|
||||
operation?: Operation
|
||||
preferences: DocumentPreferences
|
||||
previousValue?: TValue
|
||||
req: PayloadRequest
|
||||
siblingData: Partial<TSiblingData>
|
||||
}
|
||||
|
||||
export type ValidateOptions<TData, TSiblingData, TFieldConfig extends object> = BaseValidateOptions<
|
||||
export type ValidateOptions<
|
||||
TData,
|
||||
TSiblingData
|
||||
> &
|
||||
TFieldConfig
|
||||
TSiblingData,
|
||||
TFieldConfig extends object,
|
||||
TValue,
|
||||
> = BaseValidateOptions<TData, TSiblingData, TValue> & TFieldConfig
|
||||
|
||||
export type Validate<
|
||||
TValue = any,
|
||||
@@ -197,7 +199,7 @@ export type Validate<
|
||||
TFieldConfig extends object = object,
|
||||
> = (
|
||||
value: TValue,
|
||||
options: ValidateOptions<TData, TSiblingData, TFieldConfig>,
|
||||
options: ValidateOptions<TData, TSiblingData, TFieldConfig, TValue>,
|
||||
) => Promise<string | true> | string | true
|
||||
|
||||
export type ClientValidate = Omit<Validate, 'req'>
|
||||
|
||||
@@ -140,9 +140,10 @@ export const promise = async ({
|
||||
jsonError,
|
||||
operation,
|
||||
preferences: { fields: {} },
|
||||
previousValue: siblingDoc[field.name],
|
||||
req,
|
||||
siblingData: deepMergeWithSourceArrays(siblingDoc, siblingData),
|
||||
} as ValidateOptions<any, any, { jsonError: object }>)
|
||||
} as ValidateOptions<any, any, { jsonError: object }, any>)
|
||||
|
||||
if (typeof validationResult === 'string') {
|
||||
errors.push({
|
||||
|
||||
@@ -73,7 +73,7 @@ export type NodeValidation<T extends SerializedLexicalNode = SerializedLexicalNo
|
||||
node: T
|
||||
nodeValidations: Map<string, Array<NodeValidation>>
|
||||
validation: {
|
||||
options: ValidateOptions<unknown, unknown, RichTextField>
|
||||
options: ValidateOptions<unknown, unknown, RichTextField, SerializedEditorState>
|
||||
value: SerializedEditorState
|
||||
}
|
||||
}) => Promise<string | true> | string | true
|
||||
|
||||
@@ -11,7 +11,7 @@ export async function validateNodes({
|
||||
nodeValidations: Map<string, Array<NodeValidation>>
|
||||
nodes: SerializedLexicalNode[]
|
||||
validation: {
|
||||
options: ValidateOptions<unknown, unknown, RichTextField>
|
||||
options: ValidateOptions<unknown, unknown, RichTextField, SerializedEditorState>
|
||||
value: SerializedEditorState
|
||||
}
|
||||
}): Promise<string | true> {
|
||||
|
||||
59
test/field-error-states/collections/PrevValue/index.ts
Normal file
59
test/field-error-states/collections/PrevValue/index.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
import * as QueryString from 'qs-esm'
|
||||
|
||||
import { collectionSlugs } from '../../shared.js'
|
||||
|
||||
export const PrevValue: CollectionConfig = {
|
||||
slug: collectionSlugs.prevValue,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
validate: async (value, options) => {
|
||||
if (options.operation === 'create') return true
|
||||
|
||||
const query = QueryString.stringify(
|
||||
{
|
||||
where: {
|
||||
previousValueRelation: {
|
||||
in: [options.id],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
addQueryPrefix: true,
|
||||
},
|
||||
)
|
||||
|
||||
try {
|
||||
const relatedDocs = await fetch(
|
||||
`http://localhost:3000/api/${collectionSlugs.prevValueRelation}${query}`,
|
||||
{
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
).then((res) => res.json())
|
||||
if (relatedDocs.docs.length > 0 && value !== options.previousValue) {
|
||||
console.log({
|
||||
value,
|
||||
prev: options.previousValue,
|
||||
})
|
||||
return 'Doc is being referenced, cannot change title'
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
import { collectionSlugs } from '../../shared.js'
|
||||
|
||||
export const PrevValueRelation: CollectionConfig = {
|
||||
slug: collectionSlugs.prevValueRelation,
|
||||
fields: [
|
||||
{
|
||||
relationTo: collectionSlugs.prevValue,
|
||||
name: 'previousValueRelation',
|
||||
type: 'relationship',
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
import { slugs } from '../../shared.js'
|
||||
import { collectionSlugs } from '../../shared.js'
|
||||
import { ValidateDraftsOn } from '../ValidateDraftsOn/index.js'
|
||||
|
||||
export const ValidateDraftsOff: CollectionConfig = {
|
||||
...ValidateDraftsOn,
|
||||
slug: slugs.validateDraftsOff,
|
||||
slug: collectionSlugs.validateDraftsOff,
|
||||
versions: {
|
||||
drafts: true,
|
||||
},
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
import { slugs } from '../../shared.js'
|
||||
import { collectionSlugs } from '../../shared.js'
|
||||
|
||||
export const ValidateDraftsOn: CollectionConfig = {
|
||||
slug: slugs.validateDraftsOn,
|
||||
slug: collectionSlugs.validateDraftsOn,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
import { slugs } from '../../shared.js'
|
||||
import { collectionSlugs } from '../../shared.js'
|
||||
import { ValidateDraftsOn } from '../ValidateDraftsOn/index.js'
|
||||
|
||||
export const ValidateDraftsOnAndAutosave: CollectionConfig = {
|
||||
...ValidateDraftsOn,
|
||||
slug: slugs.validateDraftsOnAutosave,
|
||||
slug: collectionSlugs.validateDraftsOnAutosave,
|
||||
versions: {
|
||||
drafts: {
|
||||
autosave: true,
|
||||
|
||||
@@ -5,6 +5,8 @@ const dirname = path.dirname(filename)
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
|
||||
import { devUser } from '../credentials.js'
|
||||
import { ErrorFieldsCollection } from './collections/ErrorFields/index.js'
|
||||
import { PrevValue } from './collections/PrevValue/index.js'
|
||||
import { PrevValueRelation } from './collections/PrevValueRelation/index.js'
|
||||
import Uploads from './collections/Upload/index.js'
|
||||
import { ValidateDraftsOff } from './collections/ValidateDraftsOff/index.js'
|
||||
import { ValidateDraftsOn } from './collections/ValidateDraftsOn/index.js'
|
||||
@@ -18,6 +20,8 @@ export default buildConfigWithDefaults({
|
||||
ValidateDraftsOn,
|
||||
ValidateDraftsOff,
|
||||
ValidateDraftsOnAndAutosave,
|
||||
PrevValue,
|
||||
PrevValueRelation,
|
||||
],
|
||||
globals: [GlobalValidateDraftsOn],
|
||||
onInit: async (payload) => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { fileURLToPath } from 'url'
|
||||
import { ensureCompilationIsDone, initPageConsoleErrorCatch, saveDocAndAssert } from '../helpers.js'
|
||||
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
|
||||
import { TEST_TIMEOUT_LONG } from '../playwright.config.js'
|
||||
import { slugs } from './shared.js'
|
||||
import { collectionSlugs } from './shared.js'
|
||||
|
||||
const { beforeAll, describe } = test
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
@@ -20,13 +20,17 @@ describe('field error states', () => {
|
||||
let validateDraftsOff: AdminUrlUtil
|
||||
let validateDraftsOn: AdminUrlUtil
|
||||
let validateDraftsOnAutosave: AdminUrlUtil
|
||||
let prevValue: AdminUrlUtil
|
||||
let prevValueRelation: AdminUrlUtil
|
||||
|
||||
beforeAll(async ({ browser }, testInfo) => {
|
||||
testInfo.setTimeout(TEST_TIMEOUT_LONG)
|
||||
;({ serverURL } = await initPayloadE2ENoConfig({ dirname }))
|
||||
validateDraftsOff = new AdminUrlUtil(serverURL, slugs.validateDraftsOff)
|
||||
validateDraftsOn = new AdminUrlUtil(serverURL, slugs.validateDraftsOn)
|
||||
validateDraftsOnAutosave = new AdminUrlUtil(serverURL, slugs.validateDraftsOnAutosave)
|
||||
validateDraftsOff = new AdminUrlUtil(serverURL, collectionSlugs.validateDraftsOff)
|
||||
validateDraftsOn = new AdminUrlUtil(serverURL, collectionSlugs.validateDraftsOn)
|
||||
validateDraftsOnAutosave = new AdminUrlUtil(serverURL, collectionSlugs.validateDraftsOnAutosave)
|
||||
prevValue = new AdminUrlUtil(serverURL, collectionSlugs.prevValue)
|
||||
prevValueRelation = new AdminUrlUtil(serverURL, collectionSlugs.prevValueRelation)
|
||||
const context = await browser.newContext()
|
||||
page = await context.newPage()
|
||||
initPageConsoleErrorCatch(page)
|
||||
@@ -87,4 +91,33 @@ describe('field error states', () => {
|
||||
await saveDocAndAssert(page, '#action-save', 'error')
|
||||
})
|
||||
})
|
||||
|
||||
describe('previous values', () => {
|
||||
test('should pass previous value into validate function', async () => {
|
||||
// save original
|
||||
await page.goto(prevValue.create)
|
||||
await page.locator('#field-title').fill('original value')
|
||||
await saveDocAndAssert(page)
|
||||
await page.locator('#field-title').fill('original value 2')
|
||||
await saveDocAndAssert(page)
|
||||
|
||||
// create relation to doc
|
||||
await page.goto(prevValueRelation.create)
|
||||
await page.locator('#field-previousValueRelation .react-select').click()
|
||||
await page.locator('#field-previousValueRelation .rs__option').first().click()
|
||||
await saveDocAndAssert(page)
|
||||
|
||||
// go back to doc
|
||||
await page.goto(prevValue.list)
|
||||
await page.locator('.row-1 a').click()
|
||||
await page.locator('#field-description').fill('some description')
|
||||
await saveDocAndAssert(page)
|
||||
await page.locator('#field-title').fill('changed')
|
||||
await saveDocAndAssert(page, '#action-save', 'error')
|
||||
|
||||
// ensure value is the value before relationship association
|
||||
await page.reload()
|
||||
await expect(page.locator('#field-title')).toHaveValue('original value 2')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { GlobalConfig } from 'payload'
|
||||
|
||||
import { slugs } from '../../shared.js'
|
||||
import { globalSlugs } from '../../shared.js'
|
||||
|
||||
export const GlobalValidateDraftsOn: GlobalConfig = {
|
||||
slug: slugs.globalValidateDraftsOn,
|
||||
slug: globalSlugs.globalValidateDraftsOn,
|
||||
fields: [
|
||||
{
|
||||
name: 'group',
|
||||
|
||||
@@ -16,10 +16,15 @@ export interface Config {
|
||||
'validate-drafts-on': ValidateDraftsOn;
|
||||
'validate-drafts-off': ValidateDraftsOff;
|
||||
'validate-drafts-on-autosave': ValidateDraftsOnAutosave;
|
||||
'prev-value': PrevValue;
|
||||
'prev-value-relation': PrevValueRelation;
|
||||
users: User;
|
||||
'payload-preferences': PayloadPreference;
|
||||
'payload-migrations': PayloadMigration;
|
||||
};
|
||||
db: {
|
||||
defaultIDType: string;
|
||||
};
|
||||
globals: {
|
||||
'global-validate-drafts-on': GlobalValidateDraftsOn;
|
||||
};
|
||||
@@ -33,13 +38,16 @@ export interface UserAuthOperations {
|
||||
email: string;
|
||||
};
|
||||
login: {
|
||||
password: string;
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
registerFirstUser: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
unlock: {
|
||||
email: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
@@ -312,6 +320,27 @@ export interface ValidateDraftsOnAutosave {
|
||||
createdAt: string;
|
||||
_status?: ('draft' | 'published') | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "prev-value".
|
||||
*/
|
||||
export interface PrevValue {
|
||||
id: string;
|
||||
title: string;
|
||||
description?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "prev-value-relation".
|
||||
*/
|
||||
export interface PrevValueRelation {
|
||||
id: string;
|
||||
previousValueRelation?: (string | null) | PrevValue;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-preferences".
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
export const slugs = {
|
||||
globalValidateDraftsOn: 'global-validate-drafts-on',
|
||||
import type { CollectionSlug, GlobalSlug } from 'payload'
|
||||
|
||||
export const collectionSlugs: {
|
||||
[key: string]: CollectionSlug
|
||||
} = {
|
||||
validateDraftsOff: 'validate-drafts-off',
|
||||
validateDraftsOn: 'validate-drafts-on',
|
||||
validateDraftsOnAutosave: 'validate-drafts-on-autosave',
|
||||
prevValue: 'prev-value',
|
||||
prevValueRelation: 'prev-value-relation',
|
||||
}
|
||||
|
||||
export const globalSlugs: {
|
||||
[key: string]: GlobalSlug
|
||||
} = {
|
||||
globalValidateDraftsOn: 'global-validate-drafts-on',
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user