chore: wires up conditions for collapsibles, groups, etc
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -84,6 +84,7 @@ export const RenderField: React.FC<Props> = ({
|
||||
readOnly={readOnly}
|
||||
schemaPath={schemaPath}
|
||||
siblingPermissions={siblingPermissions}
|
||||
type={type}
|
||||
>
|
||||
{CustomField !== undefined ? CustomField : <DefaultField {...fieldComponentProps} />}
|
||||
</FieldPropsProvider>
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 || {}
|
||||
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user