chore: wires up conditions for collapsibles, groups, etc

This commit is contained in:
James
2024-04-02 10:39:52 -04:00
parent e8506cc5f1
commit c7274ba16f
7 changed files with 63 additions and 59 deletions

View File

@@ -1,4 +1,5 @@
'use client'
import type { FieldTypes } from 'payload/config'
import type { FieldPermissions } from 'payload/types'
import React from 'react'
@@ -12,9 +13,11 @@ export type FieldPropsContextType = {
siblingPermissions: {
[fieldName: string]: FieldPermissions
}
type: keyof FieldTypes
}
const FieldPropsContext = React.createContext<FieldPropsContextType>({
type: '' as keyof FieldTypes,
indexPath: '',
path: '',
permissions: {} as FieldPermissions,
@@ -33,9 +36,11 @@ export type Props = {
siblingPermissions: {
[fieldName: string]: FieldPermissions
}
type: keyof FieldTypes
}
export const FieldPropsProvider: React.FC<Props> = ({
type,
children,
indexPath,
path,
@@ -47,6 +52,7 @@ export const FieldPropsProvider: React.FC<Props> = ({
return (
<FieldPropsContext.Provider
value={{
type,
indexPath,
path,
permissions,

View File

@@ -84,6 +84,7 @@ export const RenderField: React.FC<Props> = ({
readOnly={readOnly}
schemaPath={schemaPath}
siblingPermissions={siblingPermissions}
type={type}
>
{CustomField !== undefined ? CustomField : <DefaultField {...fieldComponentProps} />}
</FieldPropsProvider>

View File

@@ -1,11 +1,5 @@
/* eslint-disable no-param-reassign */
import type {
Data,
FormField,
FormState,
NonPresentationalField,
PayloadRequest,
} from 'payload/types'
import type { Data, Field, FormField, FormState, PayloadRequest } from 'payload/types'
import ObjectIdImport from 'bson-objectid'
import { fieldAffectsData, fieldHasSubFields, tabHasName } from 'payload/types'
@@ -23,7 +17,7 @@ export type AddFieldStatePromiseArgs = {
*/
anyParentLocalized?: boolean
data: Data
field: NonPresentationalField
field: Field
fieldIndex: number
/**
* You can use this to filter down to only `localized` fields that require translation (type: text, textarea, etc.). Another plugin might want to look for only `point` type fields to do some GIS function. With the filter function you can go in like a surgeon.
@@ -76,6 +70,7 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
anyParentLocalized = false,
data,
field,
fieldIndex,
filter,
forceFullValue = false,
fullData,
@@ -348,6 +343,11 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
}
case 'group': {
if (!filter || filter(args)) {
fieldState.disableFormData = true
state[`${path}${field.name}`] = fieldState
}
await iterateFields({
id,
addErrorPathToParent,
@@ -483,6 +483,18 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
}
} else if (fieldHasSubFields(field)) {
// Handle field types that do not use names (row, etc)
if (!filter || filter(args)) {
state[`${path}_index-${fieldIndex}`] = {
disableFormData: true,
errorPaths: [],
initialValue: undefined,
passesCondition,
valid: true,
value: undefined,
}
}
await iterateFields({
id,
// passthrough parent functionality
@@ -506,12 +518,14 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
})
} else if (field.type === 'tabs') {
const promises = field.tabs.map((tab) => {
const isNamedTab = tabHasName(tab)
return iterateFields({
id,
// passthrough parent functionality
addErrorPathToParent: addErrorPathToParentArg,
anyParentLocalized: tab.localized || anyParentLocalized,
data: tabHasName(tab) ? data?.[tab.name] || {} : data,
data: isNamedTab ? data?.[tab.name] || {} : data,
fields: tab.fields,
filter,
forceFullValue,
@@ -520,7 +534,7 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
omitParents,
operation,
parentPassesCondition: passesCondition,
path: tabHasName(tab) ? `${path}${tab.name}.` : path,
path: isNamedTab ? `${path}${tab.name}.` : path,
preferences,
req,
skipConditionChecks,
@@ -530,5 +544,16 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
})
await Promise.all(promises)
} else if (field.type === 'ui') {
if (!filter || filter(args)) {
state[`${path}_index-${fieldIndex}`] = {
disableFormData: true,
errorPaths: [],
initialValue: undefined,
passesCondition,
valid: true,
value: undefined,
}
}
}
}

View File

@@ -1,4 +1,6 @@
'use client'
import type { FieldTypes } from 'payload/config'
import React from 'react'
import { Fragment } from 'react'
@@ -6,14 +8,23 @@ import { useFormFields } from '../Form/context.js'
export const WatchCondition: React.FC<{
children: React.ReactNode
indexPath: string
name?: string
path?: string
type: keyof FieldTypes
}> = (props) => {
const { name, children, path: pathFromProps } = props
const { name, type, children, indexPath, path: pathFromProps } = props
const path = typeof pathFromProps === 'string' ? pathFromProps : name
const field = useFormFields(([fields]) => (fields && fields?.[path]) || null)
let formStateID = path
if (['collapsible', 'row'].includes(type)) {
const index = indexPath.split('.').pop()
formStateID = `${path ? `${path}.` : ''}_index-${index}`
}
const field = useFormFields(([fields]) => (fields && fields?.[formStateID]) || null)
const { passesCondition } = field || {}

View File

@@ -9,11 +9,11 @@ export const withCondition = <P extends Record<string, unknown>>(
): React.FC<P> => {
const CheckForCondition: React.FC<P> = (props) => {
const { name } = props
const { path: pathFromContext } = useFieldProps()
const { type, indexPath, path: pathFromContext } = useFieldProps()
const path = pathFromContext || name
return (
<WatchCondition name={name as string} path={path as string}>
<WatchCondition indexPath={indexPath} name={name as string} path={path as string} type={type}>
<Field {...props} />
</WatchCondition>
)

View File

@@ -18,26 +18,6 @@ export const PostsCollection: CollectionConfig = {
type: 'row',
fields: [],
},
{
name: 'richText',
type: 'richText',
},
{
name: 'relationship',
type: 'relationship',
// filterOptions: ({ id }) => {
// return {
// where: [
// {
// id: {
// not_equals: id,
// },
// },
// ],
// }
// },
relationTo: ['posts'],
},
{
name: 'associatedMedia',
type: 'upload',
@@ -47,27 +27,6 @@ export const PostsCollection: CollectionConfig = {
},
relationTo: mediaSlug,
},
{
name: 'blocksField',
type: 'blocks',
blocks: [
{
slug: 'block1',
fields: [
{
name: 'group1',
type: 'group',
fields: [
{
name: 'group1Text',
type: 'text',
},
],
},
],
},
],
},
],
versions: {
drafts: true,

View File

@@ -1,13 +1,15 @@
import type { Page } from '@playwright/test'
import type { Payload } from 'payload'
import { expect, test } from '@playwright/test'
import path from 'path'
import { fileURLToPath } from 'url'
import type { PayloadTestSDK } from '../helpers/sdk/index.js'
import type { Config } from './payload-types.js'
import { exactText, initPageConsoleErrorCatch, saveDocAndAssert } from '../helpers.js'
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
import { initPayloadE2E } from '../helpers/initPayloadE2E.js'
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
import { mobileBreakpoint } from './shared.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
@@ -15,7 +17,7 @@ const dirname = path.dirname(filename)
const { beforeAll, describe } = test
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let payload: Payload
let payload: PayloadTestSDK<Config>
describe('Live Preview', () => {
let page: Page
@@ -46,7 +48,7 @@ describe('Live Preview', () => {
}
beforeAll(async ({ browser }) => {
;({ serverURL, payload } = await initPayloadE2E({ dirname }))
;({ serverURL, payload } = await initPayloadE2ENoConfig({ dirname }))
url = new AdminUrlUtil(serverURL, 'pages')
const context = await browser.newContext()
page = await context.newPage()