From 1ef1c5564d1b4b48db87678321eb3fbd4f53e8cc Mon Sep 17 00:00:00 2001 From: Tobias Odendahl Date: Fri, 2 May 2025 19:03:51 +0200 Subject: [PATCH] feat(ui): add option to open related documents in a new tab (#11939) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What? Selected documents in a relationship field can be opened in a new tab. ### Why? Related documents can be edited using the edit icon which opens the document in a drawer. Sometimes users would like to open the document in a new tab instead to e.g. modify the related document at a later point in time. This currently requires users to find the related document via the list view and open it there. There is no easy way to find and open a related document. ### How? Adds custom handling to the relationship edit button to support opening it in a new tab via middle-click, Ctrl+click, or right-click → 'Open in new tab'. --------- Co-authored-by: Jacob Fletcher --- packages/ui/src/elements/ReactSelect/types.ts | 6 +-- packages/ui/src/fields/Relationship/index.tsx | 39 ++++++++++++------ .../MultiValueLabel/index.tsx | 7 ++-- .../select-components/SingleValue/index.tsx | 7 ++-- .../collections/Relationship/e2e.spec.ts | 40 +++++++++++++++++++ test/helpers/e2e/toggleDocDrawer.ts | 8 +++- tsconfig.base.json | 2 +- 7 files changed, 85 insertions(+), 24 deletions(-) diff --git a/packages/ui/src/elements/ReactSelect/types.ts b/packages/ui/src/elements/ReactSelect/types.ts index 2f4b8592d6..58e324b1ba 100644 --- a/packages/ui/src/elements/ReactSelect/types.ts +++ b/packages/ui/src/elements/ReactSelect/types.ts @@ -1,12 +1,11 @@ import type { LabelFunction } from 'payload' import type { CommonProps, GroupBase, Props as ReactSelectStateManagerProps } from 'react-select' -import type { DocumentDrawerProps, UseDocumentDrawer } from '../DocumentDrawer/types.js' +import type { DocumentDrawerProps } from '../DocumentDrawer/types.js' type CustomSelectProps = { disableKeyDown?: boolean disableMouseDown?: boolean - DocumentDrawerToggler?: ReturnType[1] draggableProps?: any droppableRef?: React.RefObject editableProps?: ( @@ -15,10 +14,11 @@ type CustomSelectProps = { selectProps: ReactSelectStateManagerProps, ) => any onDelete?: DocumentDrawerProps['onDelete'] - onDocumentDrawerOpen?: (args: { + onDocumentOpen?: (args: { collectionSlug: string hasReadPermission: boolean id: number | string + openInNewTab?: boolean }) => void onDuplicate?: DocumentDrawerProps['onSave'] onSave?: DocumentDrawerProps['onSave'] diff --git a/packages/ui/src/fields/Relationship/index.tsx b/packages/ui/src/fields/Relationship/index.tsx index 3d093c0c66..21cbaa4299 100644 --- a/packages/ui/src/fields/Relationship/index.tsx +++ b/packages/ui/src/fields/Relationship/index.tsx @@ -7,7 +7,7 @@ import type { } from 'payload' import { dequal } from 'dequal/lite' -import { wordBoundariesRegex } from 'payload/shared' +import { formatAdminURL, wordBoundariesRegex } from 'payload/shared' import * as qs from 'qs-esm' import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react' @@ -83,7 +83,7 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) => const hasMultipleRelations = Array.isArray(relationTo) const [currentlyOpenRelationship, setCurrentlyOpenRelationship] = useState< - Parameters[0] + Parameters[0] >({ id: undefined, collectionSlug: undefined, @@ -631,16 +631,29 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) => return r.test(labelString.slice(-breakApartThreshold)) }, []) - const onDocumentDrawerOpen = useCallback< - ReactSelectAdapterProps['customProps']['onDocumentDrawerOpen'] - >(({ id, collectionSlug, hasReadPermission }) => { - openDrawerWhenRelationChanges.current = true - setCurrentlyOpenRelationship({ - id, - collectionSlug, - hasReadPermission, - }) - }, []) + const onDocumentOpen = useCallback( + ({ id, collectionSlug, hasReadPermission, openInNewTab }) => { + if (openInNewTab) { + if (hasReadPermission && id && collectionSlug) { + const docUrl = formatAdminURL({ + adminRoute: config.routes.admin, + path: `/collections/${collectionSlug}/${id}`, + }) + + window.open(docUrl, '_blank') + } + } else { + openDrawerWhenRelationChanges.current = true + + setCurrentlyOpenRelationship({ + id, + collectionSlug, + hasReadPermission, + }) + } + }, + [setCurrentlyOpenRelationship, config.routes.admin], + ) useEffect(() => { if (openDrawerWhenRelationChanges.current) { @@ -697,7 +710,7 @@ const RelationshipFieldComponent: RelationshipFieldClientComponent = (props) => customProps={{ disableKeyDown: isDrawerOpen || isListDrawerOpen, disableMouseDown: isDrawerOpen || isListDrawerOpen, - onDocumentDrawerOpen, + onDocumentOpen, onSave, }} disabled={readOnly || disabled || isDrawerOpen || isListDrawerOpen} diff --git a/packages/ui/src/fields/Relationship/select-components/MultiValueLabel/index.tsx b/packages/ui/src/fields/Relationship/select-components/MultiValueLabel/index.tsx index 03cb247a53..adc01e11d4 100644 --- a/packages/ui/src/fields/Relationship/select-components/MultiValueLabel/index.tsx +++ b/packages/ui/src/fields/Relationship/select-components/MultiValueLabel/index.tsx @@ -25,7 +25,7 @@ export const MultiValueLabel: React.FC< > = (props) => { const { data: { allowEdit, label, relationTo, value }, - selectProps: { customProps: { draggableProps, onDocumentDrawerOpen } = {} } = {}, + selectProps: { customProps: { draggableProps, onDocumentOpen } = {} } = {}, } = props const { permissions } = useAuth() @@ -49,12 +49,13 @@ export const MultiValueLabel: React.FC<