Skip to content

Commit 368cd90

Browse files
authored
fix(ui): replace deprecated document.execCommand('copy') API with navigator (#13431)
Replaces the deprecated API which might cause issues with the copy button. Also adds an E2E test for the copy button
1 parent 406a09f commit 368cd90

File tree

3 files changed

+21
-18
lines changed

3 files changed

+21
-18
lines changed

packages/ui/src/elements/CopyToClipboard/index.scss

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,6 @@
88
vertical-align: middle;
99
border-radius: 100%;
1010

11-
textarea {
12-
position: absolute;
13-
opacity: 0;
14-
z-index: -1;
15-
height: 0px;
16-
width: 0px;
17-
}
18-
1911
&:focus,
2012
&:active {
2113
outline: none;

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

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use client'
2-
import React, { useRef, useState } from 'react'
2+
import React, { useState } from 'react'
33

44
import { CopyIcon } from '../../icons/Copy/index.js'
55
import { useTranslation } from '../../providers/Translation/index.js'
@@ -15,7 +15,6 @@ export type Props = {
1515
}
1616

1717
export const CopyToClipboard: React.FC<Props> = ({ defaultMessage, successMessage, value }) => {
18-
const ref = useRef(null)
1918
const [copied, setCopied] = useState(false)
2019
const [hovered, setHovered] = useState(false)
2120
const { t } = useTranslation()
@@ -24,13 +23,9 @@ export const CopyToClipboard: React.FC<Props> = ({ defaultMessage, successMessag
2423
return (
2524
<button
2625
className={baseClass}
27-
onClick={() => {
28-
if (ref && ref.current) {
29-
ref.current.select()
30-
ref.current.setSelectionRange(0, value.length + 1)
31-
document.execCommand('copy')
32-
setCopied(true)
33-
}
26+
onClick={async () => {
27+
await navigator.clipboard.writeText(value)
28+
setCopied(true)
3429
}}
3530
onMouseEnter={() => {
3631
setHovered(true)
@@ -47,7 +42,6 @@ export const CopyToClipboard: React.FC<Props> = ({ defaultMessage, successMessag
4742
{copied && (successMessage ?? t('general:copied'))}
4843
{!copied && (defaultMessage ?? t('general:copy'))}
4944
</Tooltip>
50-
<textarea readOnly ref={ref} tabIndex={-1} value={value} />
5145
</button>
5246
)
5347
}

test/uploads/e2e.spec.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ describe('Uploads', () => {
135135
mediaWithoutDeleteAccessURL = new AdminUrlUtil(serverURL, mediaWithoutDeleteAccessSlug)
136136

137137
const context = await browser.newContext()
138+
await context.grantPermissions(['clipboard-read', 'clipboard-write'])
138139
page = await context.newPage()
139140

140141
const { consoleErrors, collectErrors, stopCollectingErrors } = initPageConsoleErrorCatch(page, {
@@ -218,6 +219,22 @@ describe('Uploads', () => {
218219
await expect(filename).toContainText('test-image.png')
219220
})
220221

222+
test('should copy the file url field to the clipboard', async () => {
223+
const mediaDoc = (
224+
await payload.find({
225+
collection: mediaSlug,
226+
depth: 0,
227+
limit: 1,
228+
pagination: false,
229+
})
230+
).docs[0]
231+
232+
await page.goto(mediaURL.edit(mediaDoc!.id))
233+
await page.locator('.copy-to-clipboard').click()
234+
const clipbaordContent = await page.evaluate(() => navigator.clipboard.readText())
235+
expect(clipbaordContent).toBe(mediaDoc?.url)
236+
})
237+
221238
test('should create file upload', async () => {
222239
await page.goto(mediaURL.create)
223240
await page.setInputFiles('input[type="file"]', path.resolve(dirname, './image.png'))

0 commit comments

Comments
 (0)