fix: join field description, custom components and loading state (#9703)

- [fix: join field shows loading when creating a
document](9f7a2e7936)
- [fix: join field
descriptions](90e8cdb464)
- [feat(ui): adds before & after inputs to join
field](19d43329ad)

---------

Co-authored-by: Patrik <patrik@payloadcms.com>
This commit is contained in:
Dan Ribbens
2024-12-03 15:58:42 -05:00
committed by GitHub
parent fbb59bab0a
commit 67179a7fb8
9 changed files with 72 additions and 5 deletions

View File

@@ -1372,6 +1372,8 @@ export type JoinField = {
admin?: {
allowCreate?: boolean
components?: {
afterInput?: CustomComponent[]
beforeInput?: CustomComponent[]
Error?: CustomComponent<JoinFieldErrorClientComponent | JoinFieldErrorServerComponent>
Label?: CustomComponent<JoinFieldLabelClientComponent | JoinFieldLabelServerComponent>
} & Admin['components']

View File

@@ -35,7 +35,9 @@ import { RelationshipTablePagination } from './Pagination.js'
const baseClass = 'relationship-table'
type RelationshipTableComponentProps = {
readonly AfterInput?: React.ReactNode
readonly allowCreate?: boolean
readonly BeforeInput?: React.ReactNode
readonly disableTable?: boolean
readonly field: JoinFieldClient
readonly filterOptions?: Where
@@ -47,7 +49,9 @@ type RelationshipTableComponentProps = {
export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (props) => {
const {
AfterInput,
allowCreate = true,
BeforeInput,
disableTable = false,
filterOptions,
initialData: initialDataFromProps,
@@ -91,7 +95,7 @@ export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (pro
() => getEntityConfig({ collectionSlug: relationTo }) as ClientCollectionConfig,
)
const [isLoadingTable, setIsLoadingTable] = useState(true)
const [isLoadingTable, setIsLoadingTable] = useState(!disableTable)
const [data, setData] = useState<PaginatedDocs>(initialData)
const [columnState, setColumnState] = useState<Column[]>()
@@ -197,6 +201,7 @@ export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (pro
</Pill>
</div>
</div>
{BeforeInput}
{isLoadingTable ? (
<p>{t('general:loading')}</p>
) : (
@@ -257,6 +262,7 @@ export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (pro
)}
</Fragment>
)}
{AfterInput}
<DocumentDrawer initialData={initialDrawerData} onSave={onDrawerCreate} />
</div>
)

View File

@@ -5,9 +5,11 @@ import type { JoinFieldClient, JoinFieldClientComponent, PaginatedDocs, Where }
import React, { useMemo } from 'react'
import { RelationshipTable } from '../../elements/RelationshipTable/index.js'
import { RenderCustomComponent } from '../../elements/RenderCustomComponent/index.js'
import { useField } from '../../forms/useField/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
import { useDocumentInfo } from '../../providers/DocumentInfo/index.js'
import { FieldDescription } from '../FieldDescription/index.js'
import { FieldLabel } from '../FieldLabel/index.js'
import { fieldBaseClass } from '../index.js'
@@ -15,7 +17,7 @@ const JoinFieldComponent: JoinFieldClientComponent = (props) => {
const {
field,
field: {
admin: { allowCreate },
admin: { allowCreate, description },
collection,
label,
localized,
@@ -27,7 +29,7 @@ const JoinFieldComponent: JoinFieldClientComponent = (props) => {
const { id: docID } = useDocumentInfo()
const { customComponents: { AfterInput, BeforeInput, Label } = {}, value } =
const { customComponents: { AfterInput, BeforeInput, Description, Label } = {}, value } =
useField<PaginatedDocs>({
path,
})
@@ -57,9 +59,10 @@ const JoinFieldComponent: JoinFieldClientComponent = (props) => {
className={[fieldBaseClass, 'join'].filter(Boolean).join(' ')}
id={`field-${path?.replace(/\./g, '__')}`}
>
{BeforeInput}
<RelationshipTable
AfterInput={AfterInput}
allowCreate={typeof docID !== 'undefined' && allowCreate}
BeforeInput={BeforeInput}
disableTable={filterOptions === null}
field={field as JoinFieldClient}
filterOptions={filterOptions}
@@ -76,7 +79,10 @@ const JoinFieldComponent: JoinFieldClientComponent = (props) => {
}
relationTo={collection}
/>
{AfterInput}
<RenderCustomComponent
CustomComponent={Description}
Fallback={<FieldDescription description={description} path={path} />}
/>
</div>
)
}

View File

@@ -47,6 +47,13 @@ export const Categories: CollectionConfig = {
name: 'relatedPosts',
label: 'Related Posts',
type: 'join',
admin: {
components: {
afterInput: ['/components/AfterInput.js#AfterInput'],
beforeInput: ['/components/BeforeInput.js#BeforeInput'],
Description: '/components/CustomDescription/index.js#FieldDescriptionComponent',
},
},
collection: postsSlug,
defaultSort: '-title',
defaultLimit: 5,
@@ -57,6 +64,9 @@ export const Categories: CollectionConfig = {
name: 'hasManyPosts',
type: 'join',
collection: postsSlug,
admin: {
description: 'Static Description',
},
on: 'categories',
},
{

View File

@@ -0,0 +1,7 @@
'use client'
import React from 'react'
export const AfterInput: React.FC = () => {
return <div className="after-input">#after-input</div>
}

View File

@@ -0,0 +1,7 @@
'use client'
import React from 'react'
export const BeforeInput: React.FC = () => {
return <div className="before-input">#before-input</div>
}

View File

@@ -0,0 +1,8 @@
'use client'
import type { FieldDescriptionClientComponent } from 'payload'
import React from 'react'
export const FieldDescriptionComponent: FieldDescriptionClientComponent = ({ path }) => {
return <div className={`field-description-${path}`}>Component description: {path}</div>
}

View File

@@ -22,6 +22,11 @@ const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export default buildConfigWithDefaults({
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [
Posts,
Categories,

View File

@@ -88,6 +88,22 @@ test.describe('Admin Panel', () => {
await page.goto(categoriesURL.create)
const nameField = page.locator('#field-name')
await expect(nameField).toBeVisible()
// assert that the join field is visible and is not stuck in a loading state
await expect(page.locator('#field-relatedPosts')).toContainText('No Posts found.')
await expect(page.locator('#field-relatedPosts')).not.toContainText('loading')
// assert that the create new button is not visible
await expect(page.locator('#field-relatedPosts > .relationship-table__add-new')).toBeHidden()
// assert that the admin.description is visible
await expect(page.locator('.field-description-hasManyPosts')).toHaveText('Static Description')
//assert that the admin.components.Description is visible
await expect(page.locator('.field-description-relatedPosts')).toHaveText(
'Component description: relatedPosts',
)
await nameField.fill('test category')
await saveDocAndAssert(page)
})