feat: Added Copy/Paste from Google Docs (#7136)

Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
Lakshya Satpal
2023-10-19 15:44:23 +05:30
committed by GitHub
parent dde3dac931
commit 63650f82d1
9 changed files with 232 additions and 77 deletions

View File

@ -35,22 +35,14 @@ vi.mock("../keys.ts", async (importOriginal) => {
};
});
const setClipboardText = (text: string) => {
Object.assign(navigator, {
clipboard: {
readText: () => text,
},
const sendPasteEvent = (text: string) => {
const clipboardEvent = createPasteEvent({
"text/plain": text,
});
};
const sendPasteEvent = (text?: string) => {
const clipboardEvent = createPasteEvent(
text || (() => window.navigator.clipboard.readText()),
);
document.dispatchEvent(clipboardEvent);
};
const pasteWithCtrlCmdShiftV = (text?: string) => {
const pasteWithCtrlCmdShiftV = (text: string) => {
Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => {
//triggering keydown with an empty clipboard
Keyboard.keyPress(KEYS.V);
@ -59,7 +51,7 @@ const pasteWithCtrlCmdShiftV = (text?: string) => {
});
};
const pasteWithCtrlCmdV = (text?: string) => {
const pasteWithCtrlCmdV = (text: string) => {
Keyboard.withModifierKeys({ ctrl: true }, () => {
//triggering keydown with an empty clipboard
Keyboard.keyPress(KEYS.V);
@ -86,7 +78,6 @@ beforeEach(async () => {
initialData={{ appState: { zoom: { value: 1 as NormalizedZoomValue } } }}
/>,
);
setClipboardText("");
Object.assign(document, {
elementFromPoint: () => GlobalTestState.canvas,
});
@ -120,8 +111,7 @@ describe("general paste behavior", () => {
describe("paste text as single lines", () => {
it("should create an element for each line when copying with Ctrl/Cmd+V", async () => {
const text = "sajgfakfn\naaksfnknas\nakefnkasf";
setClipboardText(text);
pasteWithCtrlCmdV();
pasteWithCtrlCmdV(text);
await waitFor(() => {
expect(h.elements.length).toEqual(text.split("\n").length);
});
@ -129,8 +119,7 @@ describe("paste text as single lines", () => {
it("should ignore empty lines when creating an element for each line", async () => {
const text = "\n\nsajgfakfn\n\n\naaksfnknas\n\nakefnkasf\n\n\n";
setClipboardText(text);
pasteWithCtrlCmdV();
pasteWithCtrlCmdV(text);
await waitFor(() => {
expect(h.elements.length).toEqual(3);
});
@ -138,8 +127,7 @@ describe("paste text as single lines", () => {
it("should not create any element if clipboard has only new lines", async () => {
const text = "\n\n\n\n\n";
setClipboardText(text);
pasteWithCtrlCmdV();
pasteWithCtrlCmdV(text);
await waitFor(async () => {
await sleep(50); // elements lenght will always be zero if we don't wait, since paste is async
expect(h.elements.length).toEqual(0);
@ -155,8 +143,7 @@ describe("paste text as single lines", () => {
) +
10 / h.app.state.zoom.value;
mouse.moveTo(100, 100);
setClipboardText(text);
pasteWithCtrlCmdV();
pasteWithCtrlCmdV(text);
await waitFor(async () => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [fx, firstElY] = getElementBounds(h.elements[0]);
@ -177,8 +164,7 @@ describe("paste text as single lines", () => {
) +
10 / h.app.state.zoom.value;
mouse.moveTo(100, 100);
setClipboardText(text);
pasteWithCtrlCmdV();
pasteWithCtrlCmdV(text);
await waitFor(async () => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [fx, firstElY] = getElementBounds(h.elements[0]);
@ -192,16 +178,14 @@ describe("paste text as single lines", () => {
describe("paste text as a single element", () => {
it("should create single text element when copying text with Ctrl/Cmd+Shift+V", async () => {
const text = "sajgfakfn\naaksfnknas\nakefnkasf";
setClipboardText(text);
pasteWithCtrlCmdShiftV();
pasteWithCtrlCmdShiftV(text);
await waitFor(() => {
expect(h.elements.length).toEqual(1);
});
});
it("should not create any element when only new lines in clipboard", async () => {
const text = "\n\n\n\n";
setClipboardText(text);
pasteWithCtrlCmdShiftV();
pasteWithCtrlCmdShiftV(text);
await waitFor(async () => {
await sleep(50);
expect(h.elements.length).toEqual(0);
@ -243,8 +227,7 @@ describe("Paste bound text container", () => {
type: "excalidraw/clipboard",
elements: [container, textElement],
});
setClipboardText(data);
pasteWithCtrlCmdShiftV();
pasteWithCtrlCmdShiftV(data);
await waitFor(async () => {
await sleep(1);
@ -266,8 +249,7 @@ describe("Paste bound text container", () => {
textElement,
],
});
setClipboardText(data);
pasteWithCtrlCmdShiftV();
pasteWithCtrlCmdShiftV(data);
await waitFor(async () => {
await sleep(1);

View File

@ -727,7 +727,7 @@ describe("freedraw", () => {
describe("image", () => {
const createImage = async () => {
const sendPasteEvent = (file?: File) => {
const clipboardEvent = createPasteEvent("", file ? [file] : []);
const clipboardEvent = createPasteEvent({}, file ? [file] : []);
document.dispatchEvent(clipboardEvent);
};

View File

@ -208,10 +208,8 @@ export const assertSelectedElements = (
expect(selectedElementIds).toEqual(expect.arrayContaining(ids));
};
export const createPasteEvent = (
text:
| string
| /* getData function */ ((type: string) => string | Promise<string>),
export const createPasteEvent = <T extends "text/plain" | "text/html">(
items: Record<T, string>,
files?: File[],
) => {
return Object.assign(
@ -222,11 +220,12 @@ export const createPasteEvent = (
}),
{
clipboardData: {
getData: typeof text === "string" ? () => text : text,
getData: (type: string) =>
(items as Record<string, string>)[type] || "",
files: files || [],
},
},
);
) as any as ClipboardEvent;
};
export const toggleMenu = (container: HTMLElement) => {