From 78fafdc272967dbc724889b7c2dfeb3291e01c39 Mon Sep 17 00:00:00 2001 From: Muhammad Ali Date: Mon, 25 Sep 2023 16:19:53 +0500 Subject: [PATCH] editor: convert google docs checklists on paste --- .../clipboard/clipboard-dom-parser.ts | 13 +++++++++++++ .../clipboard-dom-parser.test.ts.snap | 7 +++++++ .../tests/clipboard-dom-parser.test.ts | 19 +++++++++++++++++-- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/editor/src/extensions/clipboard/clipboard-dom-parser.ts b/packages/editor/src/extensions/clipboard/clipboard-dom-parser.ts index e6992d6cd3..874484d404 100644 --- a/packages/editor/src/extensions/clipboard/clipboard-dom-parser.ts +++ b/packages/editor/src/extensions/clipboard/clipboard-dom-parser.ts @@ -38,6 +38,7 @@ export class ClipboardDOMParser extends ProsemirrorDOMParser { parseSlice(dom: Node, options?: ParseOptions | undefined): Slice { if (dom instanceof HTMLElement || dom instanceof Document) { + convertGoogleDocsChecklist(dom); formatCodeblocks(dom); convertBrToSingleSpacedParagraphs(dom); } @@ -121,6 +122,18 @@ export function convertBrToSingleSpacedParagraphs(dom: HTMLElement | Document) { } } +export function convertGoogleDocsChecklist(dom: HTMLElement | Document) { + for (const li of dom.querySelectorAll(`ul li[role="checkbox"]`)) { + if (!li.parentElement?.classList.contains("checklist")) + li.parentElement!.classList.add("checklist"); + li.className = "checklist--item"; + if (li.firstElementChild?.tagName === "IMG") li.firstElementChild.remove(); + if (li.getAttribute("aria-checked") === "true") { + li.classList.add("checked"); + } + } +} + function splitOn(bound: Element, cutElement: Element) { let grandparent: ParentNode | null = null; for ( diff --git a/packages/editor/src/extensions/clipboard/tests/__snapshots__/clipboard-dom-parser.test.ts.snap b/packages/editor/src/extensions/clipboard/tests/__snapshots__/clipboard-dom-parser.test.ts.snap index 4dc50aea3f..efee0d3d4d 100644 --- a/packages/editor/src/extensions/clipboard/tests/__snapshots__/clipboard-dom-parser.test.ts.snap +++ b/packages/editor/src/extensions/clipboard/tests/__snapshots__/clipboard-dom-parser.test.ts.snap @@ -75,6 +75,13 @@ exports[`convert br tags to paragraphs 12`] = ` " `; +exports[`convert google docs checklist 1`] = ` +"
+ + +
" +`; + exports[`properly format codeblocks 1`] = ` "

Sure! Here's an implementation of a word counter for Thai that considers each syllable consisting of a consonant sound followed by a vowel sound as a word:

javascript
function countThaiWords(text) {
diff --git a/packages/editor/src/extensions/clipboard/tests/clipboard-dom-parser.test.ts b/packages/editor/src/extensions/clipboard/tests/clipboard-dom-parser.test.ts
index fcef59e7f9..d7af07d565 100644
--- a/packages/editor/src/extensions/clipboard/tests/clipboard-dom-parser.test.ts
+++ b/packages/editor/src/extensions/clipboard/tests/clipboard-dom-parser.test.ts
@@ -20,7 +20,8 @@ along with this program.  If not, see .
 import { test } from "vitest";
 import {
   formatCodeblocks,
-  convertBrToSingleSpacedParagraphs
+  convertBrToSingleSpacedParagraphs,
+  convertGoogleDocsChecklist
 } from "../clipboard-dom-parser";
 
 const cases = [
@@ -101,7 +102,7 @@ and yet, we somehow manage to get by.
]; for (const testCase of cases) { - const [html, expected] = testCase; + const [html] = testCase; test(`convert br tags to paragraphs`, (t) => { const element = new DOMParser().parseFromString(html, "text/html"); convertBrToSingleSpacedParagraphs(element); @@ -139,3 +140,17 @@ for (const codeBlock of codeBlocks) { t.expect(element.body.innerHTML.trim()).toMatchSnapshot(); }); } + +const checkLists = [ + `
+
  • unchecked

    Adsjkfhasdf

  • unchecked

    Asdfsadf

  • checked

    Asdfsda

  • checked

    Fasd

  • checked

    Fasd

  • unchecked

    F

  • checked

    akcasb

    • checked

      Asdf

    • unchecked

      Asdcasdc

    • unchecked

      sdac

  • unchecked

    Asdfsda

+ +
` +]; +for (const checkList of checkLists) { + test(`convert google docs checklist`, (t) => { + const element = new DOMParser().parseFromString(checkList, "text/html"); + convertGoogleDocsChecklist(element); + t.expect(element.body.innerHTML.trim()).toMatchSnapshot(); + }); +}