Skip to content

Commit 5423094

Browse files
authored
fix(clipboard): prevent default behavior on copy/cut (#866)
1 parent d3d71ac commit 5423094

File tree

8 files changed

+61
-23
lines changed

8 files changed

+61
-23
lines changed

src/clipboard/copy.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ export async function copy(this: Instance) {
1111
return
1212
}
1313

14-
this.dispatchUIEvent(target, 'copy', {
15-
clipboardData,
16-
})
17-
18-
if (this[Config].writeToClipboard) {
14+
if (
15+
this.dispatchUIEvent(target, 'copy', {
16+
clipboardData,
17+
}) &&
18+
this[Config].writeToClipboard
19+
) {
1920
await writeDataTransferToClipboard(doc, clipboardData)
2021
}
2122

src/clipboard/cut.ts

+8-16
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import {Config, Instance} from '../setup'
2-
import {
3-
copySelection,
4-
input,
5-
isEditable,
6-
writeDataTransferToClipboard,
7-
} from '../utils'
2+
import {copySelection, writeDataTransferToClipboard} from '../utils'
83

94
export async function cut(this: Instance) {
105
const doc = this[Config].document
@@ -16,16 +11,13 @@ export async function cut(this: Instance) {
1611
return
1712
}
1813

19-
this.dispatchUIEvent(target, 'cut', {
20-
clipboardData,
21-
})
22-
23-
if (isEditable(target)) {
24-
input(this[Config], target, '', 'deleteByCut')
25-
}
26-
27-
if (this[Config].writeToClipboard) {
28-
await writeDataTransferToClipboard(doc, clipboardData)
14+
if (
15+
this.dispatchUIEvent(target, 'cut', {
16+
clipboardData,
17+
}) &&
18+
this[Config].writeToClipboard
19+
) {
20+
await writeDataTransferToClipboard(target.ownerDocument, clipboardData)
2921
}
3022

3123
return clipboardData

src/event/behavior/cut.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import {input, isEditable} from '../../utils'
2+
import {behavior} from './registry'
3+
4+
behavior.cut = (event, target, config) => {
5+
return () => {
6+
if (isEditable(target)) {
7+
input(config, target, '', 'deleteByCut')
8+
}
9+
}
10+
}

src/event/behavior/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import './click'
2+
import './cut'
23
import './keydown'
34
import './keypress'
45
import './keyup'

tests/_helpers/listeners.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export function addListeners(
9393
eventHandlerCalls = []
9494
}
9595

96-
function eventWasFired(eventType: keyof GlobalEventHandlersEventMap) {
96+
function eventWasFired(eventType: keyof DocumentEventMap) {
9797
return getEvents(eventType).length > 0
9898
}
9999

tests/clipboard/copy.ts

+16
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,22 @@ test('copy on empty selection does nothing', async () => {
5353
expect(getEvents()).toHaveLength(0)
5454
})
5555

56+
test('prevent default behavior per event handler', async () => {
57+
const {element, eventWasFired, getEvents, user} = setup(
58+
`<input value="bar"/>`,
59+
{
60+
selection: {anchorOffset: 0, focusOffset: 3},
61+
},
62+
)
63+
element.addEventListener('copy', e => e.preventDefault())
64+
await window.navigator.clipboard.writeText('foo')
65+
66+
await user.copy()
67+
expect(eventWasFired('copy')).toBe(true)
68+
expect(getEvents('copy')[0].clipboardData?.getData('text')).toBe('bar')
69+
await expect(window.navigator.clipboard.readText()).resolves.toBe('foo')
70+
})
71+
5672
describe('without Clipboard API', () => {
5773
beforeEach(() => {
5874
Object.defineProperty(window.navigator, 'clipboard', {

tests/clipboard/cut.ts

+17
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,23 @@ test('cut on empty selection does nothing', async () => {
6060
expect(getEvents()).toHaveLength(0)
6161
})
6262

63+
test('prevent default behavior per event handler', async () => {
64+
const {element, eventWasFired, getEvents, user} = setup(
65+
`<input value="bar"/>`,
66+
{
67+
selection: {anchorOffset: 0, focusOffset: 3},
68+
},
69+
)
70+
element.addEventListener('cut', e => e.preventDefault())
71+
await window.navigator.clipboard.writeText('foo')
72+
73+
await user.cut()
74+
expect(eventWasFired('cut')).toBe(true)
75+
expect(getEvents('cut')[0].clipboardData?.getData('text')).toBe('bar')
76+
expect(eventWasFired('input')).toBe(false)
77+
await expect(window.navigator.clipboard.readText()).resolves.toBe('foo')
78+
})
79+
6380
describe('without Clipboard API', () => {
6481
beforeEach(() => {
6582
Object.defineProperty(window.navigator, 'clipboard', {

tests/event/behavior/keypress.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ cases(
4444
expect(getEvents('input')[0]).toHaveProperty('inputType', inputType)
4545
if (expectedValue !== undefined) {
4646
expect(element).toHaveValue(expectedValue)
47-
} else if (expectedHtml !== undefined) {
47+
}
48+
if (expectedHtml !== undefined) {
4849
expect(element).toHaveProperty('innerHTML', expectedHtml)
4950
}
5051
},

0 commit comments

Comments
 (0)