Skip to content

Commit 50cc904

Browse files
tak-ambossjacobsfletch
authored andcommitted
feat(ui): add document link to drawer (#12036)
### What? Adds an option to open the current document in a new tab when opened in a drawer. ### Why? There is currently no direct way to open a document when opened in a drawer. However, sometimes editors want to edit one or multiple documents from relationships independently of the current edit view and need an easy option to open these separately. ### How? Converts the document id to a link if in drawer context. ![image](https://github.com/user-attachments/assets/e448328f-f685-49b8-95c5-bd5d6aa60e35) --------- Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
1 parent 6cd873c commit 50cc904

File tree

3 files changed

+88
-8
lines changed

3 files changed

+88
-8
lines changed

packages/ui/src/elements/IDLabel/index.tsx

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,39 @@
22
import React from 'react'
33

44
import './index.scss'
5+
import { Link } from '../../elements/Link/index.js'
6+
import { useConfig } from '../../providers/Config/index.js'
7+
import { useDocumentInfo } from '../../providers/DocumentInfo/index.js'
8+
import { formatAdminURL } from '../../utilities/formatAdminURL.js'
59
import { sanitizeID } from '../../utilities/sanitizeID.js'
10+
import { useDrawerDepth } from '../Drawer/index.js'
611

712
const baseClass = 'id-label'
813

914
export const IDLabel: React.FC<{ className?: string; id: string; prefix?: string }> = ({
1015
id,
1116
className,
1217
prefix = 'ID:',
13-
}) => (
14-
<div className={[baseClass, className].filter(Boolean).join(' ')} title={id}>
15-
{prefix}
16-
&nbsp;
17-
{sanitizeID(id)}
18-
</div>
19-
)
18+
}) => {
19+
const {
20+
config: {
21+
routes: { admin: adminRoute },
22+
},
23+
} = useConfig()
24+
25+
const { collectionSlug, globalSlug } = useDocumentInfo()
26+
const drawerDepth = useDrawerDepth()
27+
28+
const docPath = formatAdminURL({
29+
adminRoute,
30+
path: `/${collectionSlug ? `collections/${collectionSlug}` : `globals/${globalSlug}`}/${id}`,
31+
})
32+
33+
return (
34+
<div className={[baseClass, className].filter(Boolean).join(' ')} title={id}>
35+
{prefix}
36+
&nbsp;
37+
{drawerDepth > 1 ? <Link href={docPath}>{sanitizeID(id)}</Link> : sanitizeID(id)}
38+
</div>
39+
)
40+
}

test/admin/e2e/document-view/e2e.spec.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,65 @@ describe('Document View', () => {
360360

361361
await expect.poll(() => drawer2Left > drawerLeft).toBe(true)
362362
})
363+
364+
test('document drawer displays a link to document', async () => {
365+
await navigateToDoc(page, postsUrl)
366+
367+
// change the relationship to a document which is a different one than the current one
368+
await page.locator('#field-relationship').click()
369+
await page.locator('#field-relationship .rs__option').nth(2).click()
370+
await saveDocAndAssert(page)
371+
372+
// open relationship drawer
373+
await page
374+
.locator('.field-type.relationship .relationship--single-value__drawer-toggler')
375+
.click()
376+
377+
const drawer1Content = page.locator('[id^=doc-drawer_posts_1_] .drawer__content')
378+
await expect(drawer1Content).toBeVisible()
379+
380+
// modify the title to trigger the leave page modal
381+
await page.locator('.drawer__content #field-title').fill('New Title')
382+
383+
// Open link in a new tab by holding down the Meta or Control key
384+
const documentLink = page.locator('.id-label a')
385+
const documentId = String(await documentLink.textContent())
386+
await documentLink.click()
387+
388+
const leavePageModal = page.locator('#leave-without-saving #confirm-action').last()
389+
await expect(leavePageModal).toBeVisible()
390+
391+
await leavePageModal.click()
392+
await page.waitForURL(postsUrl.edit(documentId))
393+
})
394+
395+
test('document can be opened in a new tab from within the drawer', async () => {
396+
await navigateToDoc(page, postsUrl)
397+
await page
398+
.locator('.field-type.relationship .relationship--single-value__drawer-toggler')
399+
.click()
400+
await wait(500)
401+
const drawer1Content = page.locator('[id^=doc-drawer_posts_1_] .drawer__content')
402+
await expect(drawer1Content).toBeVisible()
403+
404+
const currentUrl = page.url()
405+
406+
// Open link in a new tab by holding down the Meta or Control key
407+
const documentLink = page.locator('.id-label a')
408+
const documentId = String(await documentLink.textContent())
409+
const [newPage] = await Promise.all([
410+
page.context().waitForEvent('page'),
411+
documentLink.click({ modifiers: ['ControlOrMeta'] }),
412+
])
413+
414+
// Wait for navigation to complete in the new tab and ensure correct URL
415+
await expect(newPage.locator('.doc-header')).toBeVisible()
416+
// using contain here, because after load the lists view will add query params like "?limit=10"
417+
expect(newPage.url()).toContain(postsUrl.edit(documentId))
418+
419+
// Ensure the original page did not change
420+
expect(page.url()).toBe(currentUrl)
421+
})
363422
})
364423

365424
describe('descriptions', () => {

tsconfig.base.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
}
3232
],
3333
"paths": {
34-
"@payload-config": ["./test/fields/config.ts"],
34+
"@payload-config": ["./test/_community/config.ts"],
3535
"@payloadcms/admin-bar": ["./packages/admin-bar/src"],
3636
"@payloadcms/live-preview": ["./packages/live-preview/src"],
3737
"@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"],

0 commit comments

Comments
 (0)