From d766b1904c79e120687388703f01e769f0b39fb0 Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Fri, 21 Feb 2025 17:08:05 -0500 Subject: [PATCH] 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. --- .../features/relationship/client/drawer/index.tsx | 7 ++++--- .../src/features/upload/client/drawer/index.tsx | 7 ++++--- .../field/elements/relationship/Button/index.tsx | 11 +++++------ .../field/elements/relationship/Element/index.tsx | 8 +++++--- .../src/field/elements/upload/Button/index.tsx | 8 +++++--- .../src/field/elements/upload/Element/index.tsx | 9 +++++---- .../ui/src/elements/ListDrawer/DrawerContent.tsx | 2 ++ packages/ui/src/elements/ListDrawer/Provider.tsx | 13 +++++++++++-- .../TableColumns/RenderDefaultCell/index.tsx | 1 + packages/ui/src/fields/Upload/Input.tsx | 10 +++++----- 10 files changed, 47 insertions(+), 29 deletions(-) diff --git a/packages/richtext-lexical/src/features/relationship/client/drawer/index.tsx b/packages/richtext-lexical/src/features/relationship/client/drawer/index.tsx index 5f60d1667..11b0b9c44 100644 --- a/packages/richtext-lexical/src/features/relationship/client/drawer/index.tsx +++ b/packages/richtext-lexical/src/features/relationship/client/drawer/index.tsx @@ -1,4 +1,5 @@ 'use client' +import type { ListDrawerProps } from '@payloadcms/ui' import type { LexicalEditor } from 'lexical' import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js' @@ -67,13 +68,13 @@ const RelationshipDrawerComponent: React.FC = ({ enabledCollectionSlugs } ) }, [editor, openListDrawer]) - const onSelect = useCallback( - ({ collectionSlug, docID }: { collectionSlug: string; docID: number | string }) => { + const onSelect = useCallback>( + ({ collectionSlug, doc }) => { insertRelationship({ editor, relationTo: collectionSlug, replaceNodeKey, - value: docID, + value: doc.id, }) closeListDrawer() }, diff --git a/packages/richtext-lexical/src/features/upload/client/drawer/index.tsx b/packages/richtext-lexical/src/features/upload/client/drawer/index.tsx index b3b7645f5..d384c0adb 100644 --- a/packages/richtext-lexical/src/features/upload/client/drawer/index.tsx +++ b/packages/richtext-lexical/src/features/upload/client/drawer/index.tsx @@ -1,4 +1,5 @@ 'use client' +import type { ListDrawerProps } from '@payloadcms/ui' import type { LexicalEditor } from 'lexical' import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js' @@ -77,14 +78,14 @@ const UploadDrawerComponent: React.FC = ({ enabledCollectionSlugs }) => { ) }, [editor, openListDrawer]) - const onSelect = useCallback( - ({ collectionSlug, docID }: { collectionSlug: string; docID: number | string }) => { + const onSelect = useCallback>( + ({ collectionSlug, doc }) => { closeListDrawer() insertUpload({ editor, relationTo: collectionSlug, replaceNodeKey, - value: docID, + value: doc.id, }) }, [editor, closeListDrawer, replaceNodeKey], diff --git a/packages/richtext-slate/src/field/elements/relationship/Button/index.tsx b/packages/richtext-slate/src/field/elements/relationship/Button/index.tsx index 103b221a1..b851227f0 100644 --- a/packages/richtext-slate/src/field/elements/relationship/Button/index.tsx +++ b/packages/richtext-slate/src/field/elements/relationship/Button/index.tsx @@ -1,7 +1,8 @@ 'use client' +import type { ListDrawerProps } 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 { RelationshipIcon } from '../../../icons/Relationship/index.js' @@ -34,15 +35,13 @@ type Props = { const RelationshipButtonComponent: React.FC = ({ enabledCollectionSlugs }) => { const { t } = useTranslation() const editor = useSlate() - const [selectedCollectionSlug, setSelectedCollectionSlug] = useState( - () => enabledCollectionSlugs[0], - ) - const [ListDrawer, ListDrawerToggler, { closeDrawer, isDrawerOpen }] = useListDrawer({ + const [selectedCollectionSlug] = useState(() => enabledCollectionSlugs[0]) + const [ListDrawer, ListDrawerToggler, { closeDrawer }] = useListDrawer({ collectionSlugs: enabledCollectionSlugs, selectedCollection: selectedCollectionSlug, }) - const onSelect = useCallback( + const onSelect = useCallback>( ({ collectionSlug, docID }) => { insertRelationship(editor, { relationTo: collectionSlug, diff --git a/packages/richtext-slate/src/field/elements/relationship/Element/index.tsx b/packages/richtext-slate/src/field/elements/relationship/Element/index.tsx index 321ae45a1..d31cb5e42 100644 --- a/packages/richtext-slate/src/field/elements/relationship/Element/index.tsx +++ b/packages/richtext-slate/src/field/elements/relationship/Element/index.tsx @@ -1,5 +1,7 @@ 'use client' +import type { ListDrawerProps } from '@payloadcms/ui' + import { getTranslation } from '@payloadcms/translations' import { Button, @@ -103,8 +105,8 @@ const RelationshipElementComponent: React.FC = () => { [editor, element, relatedCollection, cacheBust, setParams, closeDrawer], ) - const swapRelationship = React.useCallback( - ({ collectionSlug, docID }) => { + const swapRelationship = useCallback>( + ({ collectionSlug, doc }) => { const elementPath = ReactEditor.findPath(editor, element) Transforms.setNodes( @@ -113,7 +115,7 @@ const RelationshipElementComponent: React.FC = () => { type: 'relationship', children: [{ text: ' ' }], relationTo: collectionSlug, - value: { id: docID }, + value: { id: doc.id }, }, { at: elementPath }, ) diff --git a/packages/richtext-slate/src/field/elements/upload/Button/index.tsx b/packages/richtext-slate/src/field/elements/upload/Button/index.tsx index 7d6d9cfe8..fd9266928 100644 --- a/packages/richtext-slate/src/field/elements/upload/Button/index.tsx +++ b/packages/richtext-slate/src/field/elements/upload/Button/index.tsx @@ -1,5 +1,7 @@ 'use client' +import type { ListDrawerProps } from '@payloadcms/ui' + import { useListDrawer, useTranslation } from '@payloadcms/ui' import React, { Fragment, useCallback } from 'react' import { ReactEditor, useSlate } from 'slate-react' @@ -41,12 +43,12 @@ const UploadButton: React.FC = ({ enabledCollectionSlugs }) => { uploads: true, }) - const onSelect = useCallback( - ({ collectionSlug, docID }) => { + const onSelect = useCallback>( + ({ collectionSlug, doc }) => { insertUpload(editor, { relationTo: collectionSlug, value: { - id: docID, + id: doc.id, }, }) closeDrawer() diff --git a/packages/richtext-slate/src/field/elements/upload/Element/index.tsx b/packages/richtext-slate/src/field/elements/upload/Element/index.tsx index 6f2671cb0..9f1d63b0d 100644 --- a/packages/richtext-slate/src/field/elements/upload/Element/index.tsx +++ b/packages/richtext-slate/src/field/elements/upload/Element/index.tsx @@ -1,5 +1,6 @@ 'use client' +import type { ListDrawerProps } from '@payloadcms/ui' import type { ClientCollectionConfig } from 'payload' import { getTranslation } from '@payloadcms/translations' @@ -23,8 +24,8 @@ import type { UploadElementType } from '../types.js' import { useElement } from '../../../providers/ElementProvider.js' import { EnabledRelationshipsCondition } from '../../EnabledRelationshipsCondition.js' import { uploadFieldsSchemaPath, uploadName } from '../shared.js' -import './index.scss' import { UploadDrawer } from './UploadDrawer/index.js' +import './index.scss' const baseClass = 'rich-text-upload' @@ -110,13 +111,13 @@ const UploadElementComponent: React.FC<{ enabledCollectionSlugs?: string[] }> = [editor, element, setParams, cacheBust, closeDrawer], ) - const swapUpload = React.useCallback( - ({ collectionSlug, docID }) => { + const swapUpload = useCallback>( + ({ collectionSlug, doc }) => { const newNode = { type: uploadName, children: [{ text: ' ' }], relationTo: collectionSlug, - value: { id: docID }, + value: { id: doc.id }, } const elementPath = ReactEditor.findPath(editor, element) diff --git a/packages/ui/src/elements/ListDrawer/DrawerContent.tsx b/packages/ui/src/elements/ListDrawer/DrawerContent.tsx index 9b33597db..3e7415170 100644 --- a/packages/ui/src/elements/ListDrawer/DrawerContent.tsx +++ b/packages/ui/src/elements/ListDrawer/DrawerContent.tsx @@ -111,6 +111,7 @@ export const ListDrawerContent: React.FC = ({ [ serverFunction, closeModal, + allowCreate, drawerSlug, isOpen, enableRowSelections, @@ -130,6 +131,7 @@ export const ListDrawerContent: React.FC = ({ if (typeof onSelect === 'function') { onSelect({ collectionSlug: selectedOption.value, + doc, docID: doc.id, }) } diff --git a/packages/ui/src/elements/ListDrawer/Provider.tsx b/packages/ui/src/elements/ListDrawer/Provider.tsx index 71cd4d1e5..c66f00968 100644 --- a/packages/ui/src/elements/ListDrawer/Provider.tsx +++ b/packages/ui/src/elements/ListDrawer/Provider.tsx @@ -1,4 +1,4 @@ -import type { CollectionSlug, ListQuery } from 'payload' +import type { CollectionSlug, Data, ListQuery } from 'payload' import { createContext, useContext } from 'react' @@ -14,7 +14,16 @@ export type ListDrawerContextProps = { readonly enabledCollections?: CollectionSlug[] readonly onBulkSelect?: (selected: ReturnType['selected']) => 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 readonly setSelectedOption?: (option: Option) => void } diff --git a/packages/ui/src/elements/TableColumns/RenderDefaultCell/index.tsx b/packages/ui/src/elements/TableColumns/RenderDefaultCell/index.tsx index 1ccb1b4f0..ee52e1fb7 100644 --- a/packages/ui/src/elements/TableColumns/RenderDefaultCell/index.tsx +++ b/packages/ui/src/elements/TableColumns/RenderDefaultCell/index.tsx @@ -36,6 +36,7 @@ export const RenderDefaultCell: React.FC<{ if (typeof onSelect === 'function') { onSelect({ collectionSlug: rowColl, + doc: rowData, docID: rowData.id as string, }) } diff --git a/packages/ui/src/fields/Upload/Input.tsx b/packages/ui/src/fields/Upload/Input.tsx index 57458cae8..878e8e755 100644 --- a/packages/ui/src/fields/Upload/Input.tsx +++ b/packages/ui/src/fields/Upload/Input.tsx @@ -350,9 +350,9 @@ export function UploadInput(props: UploadInputProps) { [closeCreateDocDrawer, activeRelationTo, onChange], ) - const onListSelect = React.useCallback>( - async ({ collectionSlug, docID }) => { - const loadedDocs = await populateDocs([docID], collectionSlug) + const onListSelect = useCallback>( + async ({ collectionSlug, doc }) => { + const loadedDocs = await populateDocs([doc.id], collectionSlug) const selectedDoc = loadedDocs ? loadedDocs.docs?.[0] : null setPopulatedDocs((currentDocs) => { if (selectedDoc) { @@ -375,9 +375,9 @@ export function UploadInput(props: UploadInputProps) { return currentDocs }) if (hasMany) { - onChange([...(Array.isArray(value) ? value : []), docID]) + onChange([...(Array.isArray(value) ? value : []), doc.id]) } else { - onChange(docID) + onChange(doc.id) } closeListDrawer() },