payloadcms / payload

Payload is the open-source, fullstack Next.js framework, giving you instant backend superpowers. Get a full TypeScript backend and admin panel instantly. Use Payload as a headless CMS or for building powerful applications.
https://payloadcms.com
MIT License
24.54k stars 1.56k forks source link

Custom components for collection List views cannot have items selected in ListDrawer #4990

Open 58bits opened 8 months ago

58bits commented 8 months ago

Link to reproduction

https://github.com/infonomic/payload-gallery-view

Describe the Bug

As described here in the docs - https://payloadcms.com/docs/admin/components#collection-views - Collections can implement custom list views.

However, unless the custom list view is also implemented via a Table component, there is no way for the list view to receive the onSelect event handler from the ListDrawer . This will happen when another collection wants to use the collection with the custom list view as a relationTo field and during 'Select Existing' (in our case, a custom list view in a Photos upload collection).

This is because at the moment ListDrawer - specifically DrawerContent will only pass the drawer onSelect handler to the first cell of a Table component via the TableColumnsProvider

https://github.com/payloadcms/payload/blob/b392d656fe4d52f04cf88d9003a396b2e03b5ec7/packages/payload/src/admin/components/elements/ListDrawer/DrawerContent.tsx#L231

I believe there is an easy and non-breaking fix for this - which is to pass the onSelect handler as an optional prop to the List props - which in the case of DrawerContent - are sent to the list view here....

https://github.com/payloadcms/payload/blob/b392d656fe4d52f04cf88d9003a396b2e03b5ec7/packages/payload/src/admin/components/elements/ListDrawer/DrawerContent.tsx#L302

And so all that would need to change, is to have an extra 'optional' prop added to List props here -

https://github.com/payloadcms/payload/blob/b392d656fe4d52f04cf88d9003a396b2e03b5ec7/packages/payload/src/admin/components/views/collections/List/types.ts#L9

For example - see the new props at the end of this prop list....

export type Props = {
  collection: SanitizedCollectionConfig
  customHeader?: React.ReactNode
  data: PaginatedDocs<any>
  handleDelete?: () => void
  handlePageChange?: PaginatorProps['onChange']
  handlePerPageChange?: PerPageProps['handleChange']
  handleSearchChange?: ListControlsProps['handleSearchChange']
  handleSortChange?: ListControlsProps['handleSortChange']
  handleWhereChange?: ListControlsProps['handleWhereChange']
  hasCreatePermission: boolean
  limit: number
  modifySearchParams?: boolean
  newDocumentURL: string
  onCreateNewClick?: () => void
  resetParams: (overrides?: {
    page?: number
    search?: string
    sort?: string
    where?: Where
  }) => void
  setLimit: (limit: number) => void
  setListControls: (controls: unknown) => void
  setSort: (sort: string) => void
  titleField?: FieldAffectingData
  toggleColumn: (column: string) => void
+ onSelect?:  ({collectionConfig: SanitizedCollectionConfig, docID: string}) => void
+ source?: "list-drawer" | "collection-admin"
}

I've also suggest an optional source prop where we could indicate where the list was opened from (i.e. 'list-drawer' vs 'collection-admin') although not strictly necessary.

DrawerContent could then be updated to pass the onSelect handler as follows:

  ),
      ...
      data,
      handlePageChange: setPage,
      handlePerPageChange: setLimit,
      handleSearchChange: setSearch,
      handleSortChange: setSort,
      handleWhereChange: setWhere,
      hasCreatePermission,
      limit: limit || selectedCollectionConfig?.admin?.pagination?.defaultLimit,
      modifySearchParams: false,
      newDocumentURL: null,
      setLimit,
      setSort,
      titleField,
+     onSelect: onSelect
+     source: 'list-drawer'
    }}

I realize the team at Payload are 'all hands' on Payload 3.0 - which is super exciting, but.... a little love here would be very much appreciated. It's likely going to be a little while before the 3.0 release, and even longer before we plan our migration, and we have a client that would very much like this custom list view. We've also invested a bit of time in building the PoC and reporting this issue.

An initial discussion / thread on the same topic can be found here... https://github.com/payloadcms/payload/discussions/2540 (for interest, there was an onClick handler in the 1.6v branch).

@DanRibbens @jacobsfletch

Hope this helps.

T.

To Reproduce

From the 'reproduction' repo - try to select a Photo from the Minimal collection via the relationTo field. The custom list does not have any way of recieving the onSelect handler from the ListView / DrawerContent

Payload Version

2

Adapters and Plugins

No response

58bits commented 8 months ago

Although I doubt our implementation is ready to be contributed to core, I should also add that we'd be glad to contribute this view if it might be of help. For interest, this is what our PoC custom list view looks like ... photo-gallery-og - the working repo for which is included in our 'Link to reproduction' above (almost all of which is based on the Default list view code).

58bits commented 8 months ago

Okay we've found a workaround that at least allows us to use our custom gallery view for the main 'admin collection' view - while using the default table list view for the ListDrawer modal

If the customHeader prop is populated we can be pretty sure the view was populated via the ListDrawer - and return the default list view instead. Unless there is a better way?

Would still be great to have a working solution as described above.

Hope this helps.

kendelljoseph commented 8 months ago

Hi @58bits,

I am trying to reproduce the problem. How would i get customHeader to be null? Here is the location of that variable in your code.

kendelljoseph commented 8 months ago

Oops - accidental close.

58bits commented 8 months ago

Hi @kendelljoseph - thanks for looking at this. Just comment out lines 57-59. This is the 'workaround' solution, that will render the Default table list if the list is loaded via the ListDrawer (which supplies a customHeader).

However, the real problem is that there is no place to add an onSelect handler to communicate back to the ListDrawer that we've selected a photo (unless I've missed something)

When you comment out lines 57-59 - and then try to add a photo to the 'Minimal' collection via 'Use Existing' - there's nothing you can do to 'tell' the ListDrawer that we've selected an photo.

58bits commented 8 months ago

@kendelljoseph - also if you've cloned the 'Link to reproduction' repo above - pull the latest, I've commented out lines 57-59 as well as fixed a config setting that will correctly serve thumbnails for all views.

We set this repo up fairly quickly. The .env.example settings and your .env file should also have PAYLOAD_PUBLIC_SERVER_URL=http://localhost:3000 and not port 8000.

58bits commented 8 months ago

Here's a video of the issue (again without the workaround)....

https://github.com/payloadcms/payload/assets/405612/0730ab7b-8815-4bd8-94a1-3b15058e8518

As above - if you follow the issue description - you'll see that the hyperlink around the filename to the source edit view, should 'dynamically' change to a button with an onClick => onSelect handler - as is happening in the DrawerContent via the ListDrawer and TableColumnsProvider (see the links above). We can't do this because we don't have access to the onSelect hander in our props for the view and we're not using the Table component for our custom view (because it's a custom view).

@kendelljoseph

58bits commented 8 months ago

@kendelljoseph - updated the original description above to highlight the suggested fix.

willybrauner commented 2 months ago

I just have exactly the same issue on my custom ListView when it's mounted from the Drawer. Is there a solution founded to expose the onSelect() function?