diff --git a/demos/src/Nodes/CodeBlock/React/index.html b/demos/src/Nodes/CodeBlock/React/index.html new file mode 100644 index 00000000..0896e97d --- /dev/null +++ b/demos/src/Nodes/CodeBlock/React/index.html @@ -0,0 +1,15 @@ + + + + + + + +
+ + + diff --git a/demos/src/Nodes/CodeBlock/React/index.jsx b/demos/src/Nodes/CodeBlock/React/index.jsx new file mode 100644 index 00000000..4d557fa3 --- /dev/null +++ b/demos/src/Nodes/CodeBlock/React/index.jsx @@ -0,0 +1,55 @@ +import React from 'react' +import { useEditor, EditorContent } from '@tiptap/react' +import Document from '@tiptap/extension-document' +import Paragraph from '@tiptap/extension-paragraph' +import Text from '@tiptap/extension-text' +import CodeBlock from '@tiptap/extension-code-block' +import './styles.scss' + +export default () => { + const editor = useEditor({ + extensions: [Document, Paragraph, Text, CodeBlock], + content: ` +

+ That’s a boring paragraph followed by a fenced code block: +

+
for (var i=1; i <= 20; i++)
+{
+  if (i % 15 == 0)
+    console.log("FizzBuzz");
+  else if (i % 3 == 0)
+    console.log("Fizz");
+  else if (i % 5 == 0)
+    console.log("Buzz");
+  else
+    console.log(i);
+}
+

+ Press Command/Ctrl + Enter to leave the fenced code block and continue typing in boring paragraphs. +

+ `, + }) + + if (!editor) { + return null + } + + return ( + <> + + + + + + ) +} diff --git a/demos/src/Nodes/CodeBlock/React/index.spec.js b/demos/src/Nodes/CodeBlock/React/index.spec.js new file mode 100644 index 00000000..8272768b --- /dev/null +++ b/demos/src/Nodes/CodeBlock/React/index.spec.js @@ -0,0 +1,166 @@ +context('/src/Nodes/CodeBlock/React/', () => { + before(() => { + cy.visit('/src/Nodes/CodeBlock/React/') + }) + + beforeEach(() => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.setContent('

Example Text

') + cy.get('.ProseMirror').type('{selectall}') + }) + }) + + it('should parse code blocks correctly', () => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.setContent('
Example Text
') + expect(editor.getHTML()).to.eq('
Example Text
') + }) + }) + + it('should parse code blocks with language correctly', () => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.setContent('
Example Text
') + expect(editor.getHTML()).to.eq('
Example Text
') + }) + }) + + it('the button should make the selected line a code block', () => { + cy.get('button:first').click() + + cy.get('.ProseMirror').find('pre').should('contain', 'Example Text') + }) + + it('the button should toggle the code block', () => { + cy.get('button:first').click() + + cy.get('.ProseMirror').find('pre').should('contain', 'Example Text') + + cy.get('.ProseMirror').type('{selectall}') + + cy.get('button:first').click() + + cy.get('.ProseMirror pre').should('not.exist') + }) + + it('the keyboard shortcut should make the selected line a code block', () => { + cy.get('.ProseMirror') + .trigger('keydown', { modKey: true, altKey: true, key: 'c' }) + .find('pre') + .should('contain', 'Example Text') + }) + + it('the keyboard shortcut should toggle the code block', () => { + cy.get('.ProseMirror') + .trigger('keydown', { modKey: true, altKey: true, key: 'c' }) + .find('pre') + .should('contain', 'Example Text') + + cy.get('.ProseMirror') + .type('{selectall}') + .trigger('keydown', { modKey: true, altKey: true, key: 'c' }) + + cy.get('.ProseMirror pre').should('not.exist') + }) + + it('should parse the language from a HTML code block', () => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.setContent( + '
body { display: none; }
', + ) + + cy.get('.ProseMirror').find('pre>code.language-css').should('have.length', 1) + }) + }) + + it('should make a code block from backtick markdown shortcuts', () => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.clearContent() + + cy.get('.ProseMirror').type('``` Code').find('pre>code').should('contain', 'Code') + }) + }) + + it('should make a code block from tilde markdown shortcuts', () => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.clearContent() + + cy.get('.ProseMirror').type('~~~ Code').find('pre>code').should('contain', 'Code') + }) + }) + + it('should make a code block for js with backticks', () => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.clearContent() + + cy.get('.ProseMirror') + .type('```js Code') + .find('pre>code.language-js') + .should('contain', 'Code') + }) + }) + + it('should make a code block for js with tildes', () => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.clearContent() + + cy.get('.ProseMirror') + .type('~~~js Code') + .find('pre>code.language-js') + .should('contain', 'Code') + }) + }) + + it('should make a code block from backtick markdown shortcuts followed by enter', () => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.clearContent() + + cy.get('.ProseMirror').type('```{enter}Code').find('pre>code').should('contain', 'Code') + }) + }) + + it('reverts the markdown shortcut when pressing backspace', () => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.clearContent() + + cy.get('.ProseMirror').type('``` {backspace}') + + cy.get('.ProseMirror pre').should('not.exist') + }) + }) + + it('removes the code block when pressing backspace', () => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.clearContent() + + cy.get('.ProseMirror pre').should('not.exist') + + cy.get('.ProseMirror').type('Paragraph{enter}``` A{backspace}{backspace}') + + cy.get('.ProseMirror pre').should('not.exist') + }) + }) + + it('removes the code block when pressing backspace, even with blank lines', () => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.clearContent() + + cy.get('.ProseMirror pre').should('not.exist') + + cy.get('.ProseMirror').type('Paragraph{enter}{enter}``` A{backspace}{backspace}') + + cy.get('.ProseMirror pre').should('not.exist') + }) + }) + + it('removes the code block when pressing backspace, even at start of document', () => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.clearContent() + + cy.get('.ProseMirror pre').should('not.exist') + + cy.get('.ProseMirror').type('``` A{leftArrow}{backspace}') + + cy.get('.ProseMirror pre').should('not.exist') + }) + }) +}) diff --git a/demos/src/Nodes/CodeBlock/React/styles.scss b/demos/src/Nodes/CodeBlock/React/styles.scss new file mode 100644 index 00000000..97cd3c36 --- /dev/null +++ b/demos/src/Nodes/CodeBlock/React/styles.scss @@ -0,0 +1,21 @@ +/* Basic editor styles */ +.ProseMirror { + > * + * { + margin-top: 0.75em; + } + + pre { + background: #0d0d0d; + border-radius: 0.5rem; + color: #fff; + font-family: "JetBrainsMono", monospace; + padding: 0.75rem 1rem; + + code { + background: none; + color: inherit; + font-size: 0.8rem; + padding: 0; + } + } +}