feat(ui): threads row data through list drawer onSelect callback (#11339)
When rendering a list drawer, you can pass a custom `onSelect` callback to execute when the user clicks on the linked cell within the table. The underlying handler, however, only passes the `docID` and `collectionSlug` args through the callback, rather than the document itself. This makes it impossible to perform side-effects that require the data of the row that was selected. Instances of this callback were also largely untyped. Needed for #11330.
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
import type { ListDrawerProps } from '@payloadcms/ui'
|
||||||
import type { LexicalEditor } from 'lexical'
|
import type { LexicalEditor } from 'lexical'
|
||||||
|
|
||||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js'
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js'
|
||||||
@@ -67,13 +68,13 @@ const RelationshipDrawerComponent: React.FC<Props> = ({ enabledCollectionSlugs }
|
|||||||
)
|
)
|
||||||
}, [editor, openListDrawer])
|
}, [editor, openListDrawer])
|
||||||
|
|
||||||
const onSelect = useCallback(
|
const onSelect = useCallback<NonNullable<ListDrawerProps['onSelect']>>(
|
||||||
({ collectionSlug, docID }: { collectionSlug: string; docID: number | string }) => {
|
({ collectionSlug, doc }) => {
|
||||||
insertRelationship({
|
insertRelationship({
|
||||||
editor,
|
editor,
|
||||||
relationTo: collectionSlug,
|
relationTo: collectionSlug,
|
||||||
replaceNodeKey,
|
replaceNodeKey,
|
||||||
value: docID,
|
value: doc.id,
|
||||||
})
|
})
|
||||||
closeListDrawer()
|
closeListDrawer()
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
import type { ListDrawerProps } from '@payloadcms/ui'
|
||||||
import type { LexicalEditor } from 'lexical'
|
import type { LexicalEditor } from 'lexical'
|
||||||
|
|
||||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js'
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js'
|
||||||
@@ -77,14 +78,14 @@ const UploadDrawerComponent: React.FC<Props> = ({ enabledCollectionSlugs }) => {
|
|||||||
)
|
)
|
||||||
}, [editor, openListDrawer])
|
}, [editor, openListDrawer])
|
||||||
|
|
||||||
const onSelect = useCallback(
|
const onSelect = useCallback<NonNullable<ListDrawerProps['onSelect']>>(
|
||||||
({ collectionSlug, docID }: { collectionSlug: string; docID: number | string }) => {
|
({ collectionSlug, doc }) => {
|
||||||
closeListDrawer()
|
closeListDrawer()
|
||||||
insertUpload({
|
insertUpload({
|
||||||
editor,
|
editor,
|
||||||
relationTo: collectionSlug,
|
relationTo: collectionSlug,
|
||||||
replaceNodeKey,
|
replaceNodeKey,
|
||||||
value: docID,
|
value: doc.id,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[editor, closeListDrawer, replaceNodeKey],
|
[editor, closeListDrawer, replaceNodeKey],
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
import type { ListDrawerProps } from '@payloadcms/ui'
|
||||||
|
|
||||||
import { useListDrawer, useTranslation } from '@payloadcms/ui'
|
import { useListDrawer, useTranslation } from '@payloadcms/ui'
|
||||||
import React, { Fragment, useCallback, useEffect, useState } from 'react'
|
import React, { Fragment, useCallback, useState } from 'react'
|
||||||
import { ReactEditor, useSlate } from 'slate-react'
|
import { ReactEditor, useSlate } from 'slate-react'
|
||||||
|
|
||||||
import { RelationshipIcon } from '../../../icons/Relationship/index.js'
|
import { RelationshipIcon } from '../../../icons/Relationship/index.js'
|
||||||
@@ -34,15 +35,13 @@ type Props = {
|
|||||||
const RelationshipButtonComponent: React.FC<Props> = ({ enabledCollectionSlugs }) => {
|
const RelationshipButtonComponent: React.FC<Props> = ({ enabledCollectionSlugs }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const editor = useSlate()
|
const editor = useSlate()
|
||||||
const [selectedCollectionSlug, setSelectedCollectionSlug] = useState(
|
const [selectedCollectionSlug] = useState(() => enabledCollectionSlugs[0])
|
||||||
() => enabledCollectionSlugs[0],
|
const [ListDrawer, ListDrawerToggler, { closeDrawer }] = useListDrawer({
|
||||||
)
|
|
||||||
const [ListDrawer, ListDrawerToggler, { closeDrawer, isDrawerOpen }] = useListDrawer({
|
|
||||||
collectionSlugs: enabledCollectionSlugs,
|
collectionSlugs: enabledCollectionSlugs,
|
||||||
selectedCollection: selectedCollectionSlug,
|
selectedCollection: selectedCollectionSlug,
|
||||||
})
|
})
|
||||||
|
|
||||||
const onSelect = useCallback(
|
const onSelect = useCallback<NonNullable<ListDrawerProps['onSelect']>>(
|
||||||
({ collectionSlug, docID }) => {
|
({ collectionSlug, docID }) => {
|
||||||
insertRelationship(editor, {
|
insertRelationship(editor, {
|
||||||
relationTo: collectionSlug,
|
relationTo: collectionSlug,
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
|
import type { ListDrawerProps } from '@payloadcms/ui'
|
||||||
|
|
||||||
import { getTranslation } from '@payloadcms/translations'
|
import { getTranslation } from '@payloadcms/translations'
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -103,8 +105,8 @@ const RelationshipElementComponent: React.FC = () => {
|
|||||||
[editor, element, relatedCollection, cacheBust, setParams, closeDrawer],
|
[editor, element, relatedCollection, cacheBust, setParams, closeDrawer],
|
||||||
)
|
)
|
||||||
|
|
||||||
const swapRelationship = React.useCallback(
|
const swapRelationship = useCallback<NonNullable<ListDrawerProps['onSelect']>>(
|
||||||
({ collectionSlug, docID }) => {
|
({ collectionSlug, doc }) => {
|
||||||
const elementPath = ReactEditor.findPath(editor, element)
|
const elementPath = ReactEditor.findPath(editor, element)
|
||||||
|
|
||||||
Transforms.setNodes(
|
Transforms.setNodes(
|
||||||
@@ -113,7 +115,7 @@ const RelationshipElementComponent: React.FC = () => {
|
|||||||
type: 'relationship',
|
type: 'relationship',
|
||||||
children: [{ text: ' ' }],
|
children: [{ text: ' ' }],
|
||||||
relationTo: collectionSlug,
|
relationTo: collectionSlug,
|
||||||
value: { id: docID },
|
value: { id: doc.id },
|
||||||
},
|
},
|
||||||
{ at: elementPath },
|
{ at: elementPath },
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
|
import type { ListDrawerProps } from '@payloadcms/ui'
|
||||||
|
|
||||||
import { useListDrawer, useTranslation } from '@payloadcms/ui'
|
import { useListDrawer, useTranslation } from '@payloadcms/ui'
|
||||||
import React, { Fragment, useCallback } from 'react'
|
import React, { Fragment, useCallback } from 'react'
|
||||||
import { ReactEditor, useSlate } from 'slate-react'
|
import { ReactEditor, useSlate } from 'slate-react'
|
||||||
@@ -41,12 +43,12 @@ const UploadButton: React.FC<ButtonProps> = ({ enabledCollectionSlugs }) => {
|
|||||||
uploads: true,
|
uploads: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const onSelect = useCallback(
|
const onSelect = useCallback<NonNullable<ListDrawerProps['onSelect']>>(
|
||||||
({ collectionSlug, docID }) => {
|
({ collectionSlug, doc }) => {
|
||||||
insertUpload(editor, {
|
insertUpload(editor, {
|
||||||
relationTo: collectionSlug,
|
relationTo: collectionSlug,
|
||||||
value: {
|
value: {
|
||||||
id: docID,
|
id: doc.id,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
closeDrawer()
|
closeDrawer()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
|
import type { ListDrawerProps } from '@payloadcms/ui'
|
||||||
import type { ClientCollectionConfig } from 'payload'
|
import type { ClientCollectionConfig } from 'payload'
|
||||||
|
|
||||||
import { getTranslation } from '@payloadcms/translations'
|
import { getTranslation } from '@payloadcms/translations'
|
||||||
@@ -23,8 +24,8 @@ import type { UploadElementType } from '../types.js'
|
|||||||
import { useElement } from '../../../providers/ElementProvider.js'
|
import { useElement } from '../../../providers/ElementProvider.js'
|
||||||
import { EnabledRelationshipsCondition } from '../../EnabledRelationshipsCondition.js'
|
import { EnabledRelationshipsCondition } from '../../EnabledRelationshipsCondition.js'
|
||||||
import { uploadFieldsSchemaPath, uploadName } from '../shared.js'
|
import { uploadFieldsSchemaPath, uploadName } from '../shared.js'
|
||||||
import './index.scss'
|
|
||||||
import { UploadDrawer } from './UploadDrawer/index.js'
|
import { UploadDrawer } from './UploadDrawer/index.js'
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
const baseClass = 'rich-text-upload'
|
const baseClass = 'rich-text-upload'
|
||||||
|
|
||||||
@@ -110,13 +111,13 @@ const UploadElementComponent: React.FC<{ enabledCollectionSlugs?: string[] }> =
|
|||||||
[editor, element, setParams, cacheBust, closeDrawer],
|
[editor, element, setParams, cacheBust, closeDrawer],
|
||||||
)
|
)
|
||||||
|
|
||||||
const swapUpload = React.useCallback(
|
const swapUpload = useCallback<NonNullable<ListDrawerProps['onSelect']>>(
|
||||||
({ collectionSlug, docID }) => {
|
({ collectionSlug, doc }) => {
|
||||||
const newNode = {
|
const newNode = {
|
||||||
type: uploadName,
|
type: uploadName,
|
||||||
children: [{ text: ' ' }],
|
children: [{ text: ' ' }],
|
||||||
relationTo: collectionSlug,
|
relationTo: collectionSlug,
|
||||||
value: { id: docID },
|
value: { id: doc.id },
|
||||||
}
|
}
|
||||||
|
|
||||||
const elementPath = ReactEditor.findPath(editor, element)
|
const elementPath = ReactEditor.findPath(editor, element)
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ export const ListDrawerContent: React.FC<ListDrawerProps> = ({
|
|||||||
[
|
[
|
||||||
serverFunction,
|
serverFunction,
|
||||||
closeModal,
|
closeModal,
|
||||||
|
allowCreate,
|
||||||
drawerSlug,
|
drawerSlug,
|
||||||
isOpen,
|
isOpen,
|
||||||
enableRowSelections,
|
enableRowSelections,
|
||||||
@@ -130,6 +131,7 @@ export const ListDrawerContent: React.FC<ListDrawerProps> = ({
|
|||||||
if (typeof onSelect === 'function') {
|
if (typeof onSelect === 'function') {
|
||||||
onSelect({
|
onSelect({
|
||||||
collectionSlug: selectedOption.value,
|
collectionSlug: selectedOption.value,
|
||||||
|
doc,
|
||||||
docID: doc.id,
|
docID: doc.id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { CollectionSlug, ListQuery } from 'payload'
|
import type { CollectionSlug, Data, ListQuery } from 'payload'
|
||||||
|
|
||||||
import { createContext, useContext } from 'react'
|
import { createContext, useContext } from 'react'
|
||||||
|
|
||||||
@@ -14,7 +14,16 @@ export type ListDrawerContextProps = {
|
|||||||
readonly enabledCollections?: CollectionSlug[]
|
readonly enabledCollections?: CollectionSlug[]
|
||||||
readonly onBulkSelect?: (selected: ReturnType<typeof useSelection>['selected']) => void
|
readonly onBulkSelect?: (selected: ReturnType<typeof useSelection>['selected']) => void
|
||||||
readonly onQueryChange?: (query: ListQuery) => void
|
readonly onQueryChange?: (query: ListQuery) => void
|
||||||
readonly onSelect?: (args: { collectionSlug: CollectionSlug; docID: string }) => void
|
readonly onSelect?: (args: {
|
||||||
|
collectionSlug: CollectionSlug
|
||||||
|
doc: Data
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* The `docID` property is deprecated and will be removed in the next major version of Payload.
|
||||||
|
* Use `doc.id` instead.
|
||||||
|
*/
|
||||||
|
docID: string
|
||||||
|
}) => void
|
||||||
readonly selectedOption?: Option<string>
|
readonly selectedOption?: Option<string>
|
||||||
readonly setSelectedOption?: (option: Option<string>) => void
|
readonly setSelectedOption?: (option: Option<string>) => void
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export const RenderDefaultCell: React.FC<{
|
|||||||
if (typeof onSelect === 'function') {
|
if (typeof onSelect === 'function') {
|
||||||
onSelect({
|
onSelect({
|
||||||
collectionSlug: rowColl,
|
collectionSlug: rowColl,
|
||||||
|
doc: rowData,
|
||||||
docID: rowData.id as string,
|
docID: rowData.id as string,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -350,9 +350,9 @@ export function UploadInput(props: UploadInputProps) {
|
|||||||
[closeCreateDocDrawer, activeRelationTo, onChange],
|
[closeCreateDocDrawer, activeRelationTo, onChange],
|
||||||
)
|
)
|
||||||
|
|
||||||
const onListSelect = React.useCallback<NonNullable<ListDrawerProps['onSelect']>>(
|
const onListSelect = useCallback<NonNullable<ListDrawerProps['onSelect']>>(
|
||||||
async ({ collectionSlug, docID }) => {
|
async ({ collectionSlug, doc }) => {
|
||||||
const loadedDocs = await populateDocs([docID], collectionSlug)
|
const loadedDocs = await populateDocs([doc.id], collectionSlug)
|
||||||
const selectedDoc = loadedDocs ? loadedDocs.docs?.[0] : null
|
const selectedDoc = loadedDocs ? loadedDocs.docs?.[0] : null
|
||||||
setPopulatedDocs((currentDocs) => {
|
setPopulatedDocs((currentDocs) => {
|
||||||
if (selectedDoc) {
|
if (selectedDoc) {
|
||||||
@@ -375,9 +375,9 @@ export function UploadInput(props: UploadInputProps) {
|
|||||||
return currentDocs
|
return currentDocs
|
||||||
})
|
})
|
||||||
if (hasMany) {
|
if (hasMany) {
|
||||||
onChange([...(Array.isArray(value) ? value : []), docID])
|
onChange([...(Array.isArray(value) ? value : []), doc.id])
|
||||||
} else {
|
} else {
|
||||||
onChange(docID)
|
onChange(doc.id)
|
||||||
}
|
}
|
||||||
closeListDrawer()
|
closeListDrawer()
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user