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:
Jarrod Flesch
2024-07-29 16:28:28 -04:00
committed by GitHub
parent 354588898f
commit 3a941c7c8a
14 changed files with 177 additions and 24 deletions

View File

@@ -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'>

View File

@@ -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({

View File

@@ -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

View File

@@ -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> {

View 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',
},
],
}

View File

@@ -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',
},
],
}

View File

@@ -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,
},

View File

@@ -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',

View File

@@ -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,

View File

@@ -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) => {

View File

@@ -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')
})
})
})

View File

@@ -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',

View File

@@ -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".

View File

@@ -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',
}