mirror of
https://github.com/zadam/trilium.git
synced 2025-11-02 03:16:11 +01:00
chore(ckeditor5-mermaid): integrate the rest of the files
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import {
|
||||
_setModelData as setModelData,
|
||||
_getModelData as getModelData
|
||||
} from '@ckeditor/ckeditor5-engine';
|
||||
|
||||
import InsertMermaidCommand from '../../src/commands/insertMermaidCommand.js';
|
||||
import MermaidEditing from '../../src/mermaidediting.js';
|
||||
|
||||
/* global document */
|
||||
|
||||
describe( 'InsertMermaidCommand', () => {
|
||||
let domElement, editor, model, command;
|
||||
|
||||
beforeEach( async () => {
|
||||
domElement = document.createElement( 'div' );
|
||||
document.body.appendChild( domElement );
|
||||
|
||||
editor = await ClassicEditor.create( domElement, {
|
||||
plugins: [
|
||||
MermaidEditing,
|
||||
Paragraph
|
||||
]
|
||||
} );
|
||||
|
||||
model = editor.model;
|
||||
|
||||
command = new InsertMermaidCommand( editor );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
domElement.remove();
|
||||
return editor.destroy();
|
||||
} );
|
||||
|
||||
describe( '#isEnabled', () => {
|
||||
describe( 'should be false', () => {
|
||||
it( 'when selection is inside mermaid', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C">[]</mermaid>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.false;
|
||||
} );
|
||||
|
||||
it( 'when mermaid is selected', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'[<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.false;
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'should be true', () => {
|
||||
it( 'when text is selected', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo]</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.true;
|
||||
} );
|
||||
|
||||
it( 'when mermaid is part of the selection', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>' +
|
||||
'<paragraph>b]az</paragraph>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.true;
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'execute()', () => {
|
||||
it( 'should add sample mermaid', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo]</paragraph>'
|
||||
);
|
||||
|
||||
command.execute();
|
||||
|
||||
expect( getModelData( model, { withoutSelection: true } ) ).to.equal(
|
||||
'<mermaid displayMode="split" source="flowchart TB\nA --> B\nB --> C"></mermaid>'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
@@ -0,0 +1,113 @@
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import {
|
||||
_setModelData as setModelData,
|
||||
_getModelData as getModelData
|
||||
} from '@ckeditor/ckeditor5-engine';
|
||||
|
||||
import MermaidPreviewCommand from '../../src/commands/mermaidPreviewCommand.js';
|
||||
import MermaidEditing from '../../src/mermaidediting.js';
|
||||
|
||||
/* global document */
|
||||
|
||||
describe( 'MermaidPreviewCommand', () => {
|
||||
let domElement, editor, model, command;
|
||||
|
||||
beforeEach( async () => {
|
||||
domElement = document.createElement( 'div' );
|
||||
document.body.appendChild( domElement );
|
||||
|
||||
editor = await ClassicEditor.create( domElement, {
|
||||
plugins: [
|
||||
MermaidEditing,
|
||||
Paragraph
|
||||
]
|
||||
} );
|
||||
|
||||
model = editor.model;
|
||||
|
||||
command = new MermaidPreviewCommand( editor );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
domElement.remove();
|
||||
return editor.destroy();
|
||||
} );
|
||||
|
||||
describe( '#value', () => {
|
||||
it( 'should be true when mermaid element has displayMode attribute equal to "preview"', () => {
|
||||
setModelData( model, '<mermaid displayMode="preview" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( true );
|
||||
} );
|
||||
|
||||
it( 'should be false when mermaid element has displayMode attribute equal to "split"', () => {
|
||||
setModelData( model, '<mermaid displayMode="split" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( false );
|
||||
} );
|
||||
|
||||
it( 'should be false when mermaid element has displayMode attribute equal to "source"', () => {
|
||||
setModelData( model, '<mermaid displayMode="source" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( false );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( '#isEnabled', () => {
|
||||
describe( 'should be false', () => {
|
||||
it( 'when text is selected', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo]</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.false;
|
||||
} );
|
||||
|
||||
it( 'when mermaid is part of the selection', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>' +
|
||||
'<paragraph>b]az</paragraph>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.false;
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'should be true', () => {
|
||||
it( 'when selection is inside mermaid', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C">[]</mermaid>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.true;
|
||||
} );
|
||||
|
||||
it( 'when mermaid is selected', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'[<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.true;
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'execute()', () => {
|
||||
it( 'should change displayMode to "preview" for mermaid', () => {
|
||||
setModelData( model,
|
||||
'[<mermaid displayMode="source" source="foo"></mermaid>]'
|
||||
);
|
||||
|
||||
command.execute();
|
||||
|
||||
expect( getModelData( model ) ).to.equal(
|
||||
'[<mermaid displayMode="preview" source="foo"></mermaid>]'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
@@ -0,0 +1,113 @@
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import {
|
||||
_setModelData as setModelData,
|
||||
_getModelData as getModelData
|
||||
} from '@ckeditor/ckeditor5-engine';
|
||||
|
||||
import MermaidSourceViewCommand from '../../src/commands/mermaidSourceViewCommand.js';
|
||||
import MermaidEditing from '../../src/mermaidediting.js';
|
||||
|
||||
/* global document */
|
||||
|
||||
describe( 'MermaidSourceViewCommand', () => {
|
||||
let domElement, editor, model, command;
|
||||
|
||||
beforeEach( async () => {
|
||||
domElement = document.createElement( 'div' );
|
||||
document.body.appendChild( domElement );
|
||||
|
||||
editor = await ClassicEditor.create( domElement, {
|
||||
plugins: [
|
||||
MermaidEditing,
|
||||
Paragraph
|
||||
]
|
||||
} );
|
||||
|
||||
model = editor.model;
|
||||
|
||||
command = new MermaidSourceViewCommand( editor );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
domElement.remove();
|
||||
return editor.destroy();
|
||||
} );
|
||||
|
||||
describe( '#value', () => {
|
||||
it( 'should be true when mermaid element has displayMode attribute equal to "preview"', () => {
|
||||
setModelData( model, '<mermaid displayMode="preview" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( false );
|
||||
} );
|
||||
|
||||
it( 'should be false when mermaid element has displayMode attribute equal to "split"', () => {
|
||||
setModelData( model, '<mermaid displayMode="split" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( false );
|
||||
} );
|
||||
|
||||
it( 'should be false when mermaid element has displayMode attribute equal to "source"', () => {
|
||||
setModelData( model, '<mermaid displayMode="source" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( true );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( '#isEnabled', () => {
|
||||
describe( 'should be false', () => {
|
||||
it( 'when text is selected', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo]</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.false;
|
||||
} );
|
||||
|
||||
it( 'when mermaid is part of the selection', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>' +
|
||||
'<paragraph>b]az</paragraph>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.false;
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'should be true', () => {
|
||||
it( 'when selection is inside mermaid', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C">[]</mermaid>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.true;
|
||||
} );
|
||||
|
||||
it( 'when mermaid is selected', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'[<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.true;
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'execute()', () => {
|
||||
it( 'should add text', () => {
|
||||
setModelData( model,
|
||||
'[<mermaid displayMode="preview" source="foo"></mermaid>]'
|
||||
);
|
||||
|
||||
command.execute();
|
||||
|
||||
expect( getModelData( model ) ).to.equal(
|
||||
'[<mermaid displayMode="source" source="foo"></mermaid>]'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
@@ -0,0 +1,113 @@
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import {
|
||||
_setModelData as setModelData,
|
||||
_getModelData as getModelData
|
||||
} from '@ckeditor/ckeditor5-engine';
|
||||
|
||||
import MermaidSplitViewCommand from '../../src/commands/mermaidSplitViewCommand.js';
|
||||
import MermaidEditing from '../../src/mermaidediting.js';
|
||||
|
||||
/* global document */
|
||||
|
||||
describe( 'MermaidSplitViewCommand', () => {
|
||||
let domElement, editor, model, command;
|
||||
|
||||
beforeEach( async () => {
|
||||
domElement = document.createElement( 'div' );
|
||||
document.body.appendChild( domElement );
|
||||
|
||||
editor = await ClassicEditor.create( domElement, {
|
||||
plugins: [
|
||||
MermaidEditing,
|
||||
Paragraph
|
||||
]
|
||||
} );
|
||||
|
||||
model = editor.model;
|
||||
|
||||
command = new MermaidSplitViewCommand( editor );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
domElement.remove();
|
||||
return editor.destroy();
|
||||
} );
|
||||
|
||||
describe( '#value', () => {
|
||||
it( 'should be true when mermaid element has displayMode attribute equal to "preview"', () => {
|
||||
setModelData( model, '<mermaid displayMode="preview" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( false );
|
||||
} );
|
||||
|
||||
it( 'should be false when mermaid element has displayMode attribute equal to "split"', () => {
|
||||
setModelData( model, '<mermaid displayMode="split" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( true );
|
||||
} );
|
||||
|
||||
it( 'should be false when mermaid element has source attribute equal to "source"', () => {
|
||||
setModelData( model, '<mermaid displayMode="source" source="foo"></mermaid>' );
|
||||
|
||||
expect( command.value ).to.equal( false );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( '#isEnabled', () => {
|
||||
describe( 'should be false', () => {
|
||||
it( 'when text is selected', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo]</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.false;
|
||||
} );
|
||||
|
||||
it( 'when mermaid is part of the selection', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>[foo</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>' +
|
||||
'<paragraph>b]az</paragraph>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.false;
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'should be true', () => {
|
||||
it( 'when selection is inside mermaid', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C">[]</mermaid>'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.true;
|
||||
} );
|
||||
|
||||
it( 'when mermaid is selected', () => {
|
||||
setModelData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'[<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]'
|
||||
);
|
||||
|
||||
expect( command.isEnabled ).to.be.true;
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'execute()', () => {
|
||||
it( 'should change displayMode to "source" for mermaid', () => {
|
||||
setModelData( model,
|
||||
'[<mermaid displayMode="source" source="foo"></mermaid>]'
|
||||
);
|
||||
|
||||
command.execute();
|
||||
|
||||
expect( getModelData( model ) ).to.equal(
|
||||
'[<mermaid displayMode="split" source="foo"></mermaid>]'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
32
packages/ckeditor5-mermaid/tests/index.js
Normal file
32
packages/ckeditor5-mermaid/tests/index.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Mermaid as MermaidDll, icons } from '../src/index.js';
|
||||
import Mermaid from '../src/mermaid.js';
|
||||
|
||||
import infoIcon from './../theme/icons/info.svg';
|
||||
import insertMermaidIcon from './../theme/icons/insert.svg';
|
||||
import previewModeIcon from './../theme/icons/preview-mode.svg';
|
||||
import splitModeIcon from './../theme/icons/split-mode.svg';
|
||||
import sourceModeIcon from './../theme/icons/source-mode.svg';
|
||||
|
||||
describe( 'CKEditor5 Mermaid DLL', () => {
|
||||
it( 'exports MermaidWidget', () => {
|
||||
expect( MermaidDll ).to.equal( Mermaid );
|
||||
} );
|
||||
|
||||
describe( 'icons', () => {
|
||||
it( 'exports the "insertMermaidIcon" icon', () => {
|
||||
expect( icons.insertMermaidIcon ).to.equal( insertMermaidIcon );
|
||||
} );
|
||||
it( 'exports the "infoIcon" icon', () => {
|
||||
expect( icons.infoIcon ).to.equal( infoIcon );
|
||||
} );
|
||||
it( 'exports the "previewModeIcon" icon', () => {
|
||||
expect( icons.previewModeIcon ).to.equal( previewModeIcon );
|
||||
} );
|
||||
it( 'exports the "splitModeIcon" icon', () => {
|
||||
expect( icons.splitModeIcon ).to.equal( splitModeIcon );
|
||||
} );
|
||||
it( 'exports the "sourceModeIcon" icon', () => {
|
||||
expect( icons.sourceModeIcon ).to.equal( sourceModeIcon );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
@@ -1,17 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { Mermaid as MermaidDll, icons } from '../src/index.js';
|
||||
import Mermaid from '../src/mermaid.js';
|
||||
|
||||
import ckeditor from './../theme/icons/ckeditor.svg';
|
||||
|
||||
describe( 'CKEditor5 Mermaid DLL', () => {
|
||||
it( 'exports Mermaid', () => {
|
||||
expect( MermaidDll ).to.equal( Mermaid );
|
||||
} );
|
||||
|
||||
describe( 'icons', () => {
|
||||
it( 'exports the "ckeditor" icon', () => {
|
||||
expect( icons.ckeditor ).to.equal( ckeditor );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
67
packages/ckeditor5-mermaid/tests/manual/markdown.html
Normal file
67
packages/ckeditor5-mermaid/tests/manual/markdown.html
Normal file
@@ -0,0 +1,67 @@
|
||||
<style type="text/css">
|
||||
#editor, .ck-editor {
|
||||
/* Adjust width to the typical width in GH. */
|
||||
width: 820px !important;
|
||||
}
|
||||
|
||||
pre.markdown-output {
|
||||
background: hsl(70, 7%, 16%);
|
||||
color: hsl(0, 0%, 100%);
|
||||
display: block;
|
||||
font-size: 1em;
|
||||
font-family: Monaco, Menlo, Consolas, "Roboto Mono", "Courier New", "Ubuntu Mono", monospace;
|
||||
padding: 1.333em;
|
||||
}
|
||||
|
||||
.editor-container {
|
||||
display:flex;
|
||||
}
|
||||
|
||||
.output-container {
|
||||
padding-left: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="editor-container">
|
||||
<textarea id="editor">
|
||||
Mermaid snippet:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
A --> C
|
||||
A --> D
|
||||
B --> C
|
||||
B --> D
|
||||
```
|
||||
|
||||
More complex case:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
Alice->>John: Hello John, how are you?
|
||||
loop Healthcheck
|
||||
John->>John: Fight against hypochondria
|
||||
end
|
||||
Note right of John: Rational thoughts <br/>prevail!
|
||||
John-->>Alice: Great!
|
||||
John->>Bob: How about you?
|
||||
Bob-->>John: Jolly good!
|
||||
```
|
||||
|
||||
Javascript snippet:
|
||||
|
||||
```Javascript
|
||||
var foo = 'bar';
|
||||
|
||||
alert( foo );
|
||||
```
|
||||
</textarea>
|
||||
|
||||
<div class="output-container">
|
||||
<p>Output:</p>
|
||||
|
||||
<pre class="markdown-output"><code id="markdown-output"></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
67
packages/ckeditor5-mermaid/tests/manual/markdown.js
Normal file
67
packages/ckeditor5-mermaid/tests/manual/markdown.js
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
||||
*/
|
||||
|
||||
/* globals console, window, document */
|
||||
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Typing } from '@ckeditor/ckeditor5-typing';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import { Undo } from '@ckeditor/ckeditor5-undo';
|
||||
import { Enter } from '@ckeditor/ckeditor5-enter';
|
||||
import { Clipboard } from '@ckeditor/ckeditor5-clipboard';
|
||||
import { Link } from '@ckeditor/ckeditor5-link';
|
||||
import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles';
|
||||
import { Markdown } from '@ckeditor/ckeditor5-markdown-gfm';
|
||||
import { CodeBlock } from '@ckeditor/ckeditor5-code-block';
|
||||
|
||||
import Mermaid from '../../src/mermaid.js';
|
||||
|
||||
ClassicEditor
|
||||
.create( document.querySelector( '#editor' ), {
|
||||
plugins: [
|
||||
Markdown,
|
||||
Typing,
|
||||
Paragraph,
|
||||
Undo,
|
||||
Enter,
|
||||
Clipboard,
|
||||
Link,
|
||||
Bold,
|
||||
Italic,
|
||||
CodeBlock,
|
||||
Mermaid
|
||||
],
|
||||
toolbar: [ 'bold', 'italic', 'link', 'undo', 'redo', 'codeBlock', 'mermaid' ],
|
||||
codeBlock: {
|
||||
languages: [
|
||||
{ language: 'plaintext', label: 'Plain text', class: '' },
|
||||
{ language: 'javascript', label: 'JavaScript' },
|
||||
{ language: 'python', label: 'Python' },
|
||||
{ language: 'mermaid', label: 'Mermaid' }
|
||||
]
|
||||
}
|
||||
|
||||
} )
|
||||
.then( editor => {
|
||||
window.editor = editor;
|
||||
|
||||
setupMarkdownOutputPreview( editor );
|
||||
} )
|
||||
.catch( err => {
|
||||
console.error( err.stack );
|
||||
} );
|
||||
|
||||
function setupMarkdownOutputPreview( editor ) {
|
||||
const outputElement = document.querySelector( '#markdown-output' );
|
||||
|
||||
editor.model.document.on( 'change', () => {
|
||||
outputElement.innerText = editor.getData();
|
||||
} );
|
||||
|
||||
// Set the initial data with delay so hightlight.js doesn't catch them.
|
||||
window.setTimeout( () => {
|
||||
outputElement.innerText = editor.getData();
|
||||
}, 500 );
|
||||
}
|
||||
1
packages/ckeditor5-mermaid/tests/manual/markdown.md
Normal file
1
packages/ckeditor5-mermaid/tests/manual/markdown.md
Normal file
@@ -0,0 +1 @@
|
||||
## Mermaid widget
|
||||
38
packages/ckeditor5-mermaid/tests/manual/mermaid.html
Normal file
38
packages/ckeditor5-mermaid/tests/manual/mermaid.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<style type="text/css">
|
||||
#editor, .ck-editor {
|
||||
/* Adjust width to the typical width in GH. */
|
||||
width: 820px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="editor">
|
||||
<p>Mermaid snippet:</p>
|
||||
|
||||
<pre>
|
||||
<code class="mermaid language-mermaid">
|
||||
flowchart TB
|
||||
A --> C
|
||||
A --> D
|
||||
B --> C
|
||||
B --> D
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<p>More complex case:</p>
|
||||
|
||||
<pre>
|
||||
<code class="mermaid language-mermaid">
|
||||
sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
Alice->>John: Hello John, how are you?
|
||||
loop Healthcheck
|
||||
John->>John: Fight against hypochondria
|
||||
end
|
||||
Note right of John: Rational thoughts <br/>prevail!
|
||||
John-->>Alice: Great!
|
||||
John->>Bob: How about you?
|
||||
Bob-->>John: Jolly good!
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
50
packages/ckeditor5-mermaid/tests/manual/mermaid.js
Normal file
50
packages/ckeditor5-mermaid/tests/manual/mermaid.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
||||
*/
|
||||
|
||||
/* globals console, window, document */
|
||||
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Typing } from '@ckeditor/ckeditor5-typing';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import { Undo } from '@ckeditor/ckeditor5-undo';
|
||||
import { Enter } from '@ckeditor/ckeditor5-enter';
|
||||
import { Clipboard } from '@ckeditor/ckeditor5-clipboard';
|
||||
import { Link } from '@ckeditor/ckeditor5-link';
|
||||
import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles';
|
||||
import { CodeBlock } from '@ckeditor/ckeditor5-code-block';
|
||||
|
||||
import Mermaid from '../../src/mermaid.js';
|
||||
|
||||
ClassicEditor
|
||||
.create( document.querySelector( '#editor' ), {
|
||||
plugins: [
|
||||
Typing,
|
||||
Paragraph,
|
||||
Undo,
|
||||
Enter,
|
||||
Clipboard,
|
||||
Link,
|
||||
Bold,
|
||||
Italic,
|
||||
CodeBlock,
|
||||
Mermaid
|
||||
],
|
||||
toolbar: [ 'bold', 'italic', 'link', 'undo', 'redo', 'codeBlock', 'mermaid' ],
|
||||
codeBlock: {
|
||||
languages: [
|
||||
{ language: 'plaintext', label: 'Plain text', class: '' },
|
||||
{ language: 'javascript', label: 'JavaScript' },
|
||||
{ language: 'python', label: 'Python' },
|
||||
{ language: 'mermaid', label: 'Mermaid' }
|
||||
]
|
||||
}
|
||||
|
||||
} )
|
||||
.then( editor => {
|
||||
window.editor = editor;
|
||||
} )
|
||||
.catch( err => {
|
||||
console.error( err.stack );
|
||||
} );
|
||||
1
packages/ckeditor5-mermaid/tests/manual/mermaid.md
Normal file
1
packages/ckeditor5-mermaid/tests/manual/mermaid.md
Normal file
@@ -0,0 +1 @@
|
||||
## Mermaid widget
|
||||
47
packages/ckeditor5-mermaid/tests/mermaid.js
Normal file
47
packages/ckeditor5-mermaid/tests/mermaid.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Essentials } from '@ckeditor/ckeditor5-essentials';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import { Heading } from '@ckeditor/ckeditor5-heading';
|
||||
import { _setModelData as setModelData } from '@ckeditor/ckeditor5-engine';
|
||||
|
||||
import Mermaid from '../src/mermaid.js';
|
||||
|
||||
/* global document */
|
||||
|
||||
describe( 'Mermaid', () => {
|
||||
it( 'should be named', () => {
|
||||
expect( Mermaid.pluginName ).to.equal( 'Mermaid' );
|
||||
} );
|
||||
|
||||
describe( 'init()', () => {
|
||||
let domElement, editor;
|
||||
|
||||
beforeEach( async () => {
|
||||
domElement = document.createElement( 'div' );
|
||||
document.body.appendChild( domElement );
|
||||
|
||||
editor = await ClassicEditor.create( domElement, {
|
||||
plugins: [
|
||||
Paragraph,
|
||||
Heading,
|
||||
Essentials,
|
||||
Mermaid
|
||||
],
|
||||
toolbar: [
|
||||
'mermaid'
|
||||
]
|
||||
} );
|
||||
|
||||
setModelData( editor.model, '<paragraph>[]</paragraph>' );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
domElement.remove();
|
||||
return editor.destroy();
|
||||
} );
|
||||
|
||||
it( 'should add an icon to the toolbar', () => {
|
||||
expect( editor.ui.componentFactory.has( 'Mermaid' ) ).to.equal( true );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
@@ -1,56 +0,0 @@
|
||||
import { describe, expect, it, beforeEach, afterEach } from 'vitest';
|
||||
import { ClassicEditor, Essentials, Paragraph, Heading } from 'ckeditor5';
|
||||
import Mermaid from '../src/mermaid.js';
|
||||
|
||||
describe( 'Mermaid', () => {
|
||||
it( 'should be named', () => {
|
||||
expect( Mermaid.pluginName ).to.equal( 'Mermaid' );
|
||||
} );
|
||||
|
||||
describe( 'init()', () => {
|
||||
let domElement: HTMLElement, editor: ClassicEditor;
|
||||
|
||||
beforeEach( async () => {
|
||||
domElement = document.createElement( 'div' );
|
||||
document.body.appendChild( domElement );
|
||||
|
||||
editor = await ClassicEditor.create( domElement, {
|
||||
licenseKey: 'GPL',
|
||||
plugins: [
|
||||
Paragraph,
|
||||
Heading,
|
||||
Essentials,
|
||||
Mermaid
|
||||
],
|
||||
toolbar: [
|
||||
'mermaid'
|
||||
]
|
||||
} );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
domElement.remove();
|
||||
return editor.destroy();
|
||||
} );
|
||||
|
||||
it( 'should load Mermaid', () => {
|
||||
const myPlugin = editor.plugins.get( 'Mermaid' );
|
||||
|
||||
expect( myPlugin ).to.be.an.instanceof( Mermaid );
|
||||
} );
|
||||
|
||||
it( 'should add an icon to the toolbar', () => {
|
||||
expect( editor.ui.componentFactory.has( 'mermaid' ) ).to.equal( true );
|
||||
} );
|
||||
|
||||
it( 'should add a text into the editor after clicking the icon', () => {
|
||||
const icon = editor.ui.componentFactory.create( 'mermaid' );
|
||||
|
||||
expect( editor.getData() ).to.equal( '' );
|
||||
|
||||
icon.fire( 'execute' );
|
||||
|
||||
expect( editor.getData() ).to.equal( '<p>Hello CKEditor 5!</p>' );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
296
packages/ckeditor5-mermaid/tests/mermaidediting.js
Normal file
296
packages/ckeditor5-mermaid/tests/mermaidediting.js
Normal file
@@ -0,0 +1,296 @@
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Essentials } from '@ckeditor/ckeditor5-essentials';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import { Heading } from '@ckeditor/ckeditor5-heading';
|
||||
import { CodeBlockEditing } from '@ckeditor/ckeditor5-code-block';
|
||||
import {
|
||||
_setModelData as setModelData,
|
||||
_getModelData as getModelData,
|
||||
_getViewData as getViewData
|
||||
} from '@ckeditor/ckeditor5-engine';
|
||||
import MermaidEditing from '../src/mermaidediting.js';
|
||||
|
||||
/* global document */
|
||||
|
||||
describe( 'MermaidEditing', () => {
|
||||
it( 'should be named', () => {
|
||||
expect( MermaidEditing.pluginName ).to.equal( 'MermaidEditing' );
|
||||
} );
|
||||
|
||||
describe( 'conversion', () => {
|
||||
let domElement, editor, model;
|
||||
|
||||
beforeEach( async () => {
|
||||
domElement = document.createElement( 'div' );
|
||||
document.body.appendChild( domElement );
|
||||
|
||||
editor = await ClassicEditor.create( domElement, {
|
||||
plugins: [
|
||||
Paragraph,
|
||||
Heading,
|
||||
Essentials,
|
||||
CodeBlockEditing,
|
||||
MermaidEditing
|
||||
]
|
||||
} );
|
||||
|
||||
model = editor.model;
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
domElement.remove();
|
||||
return editor.destroy();
|
||||
} );
|
||||
|
||||
describe( 'conversion', () => {
|
||||
describe( 'upcast', () => {
|
||||
it( 'works correctly', () => {
|
||||
editor.setData(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid">flowchart TB\nA --> B\nB --> C</code>' +
|
||||
'</pre>'
|
||||
);
|
||||
|
||||
expect( getModelData( model, { withoutSelection: true } ) ).to.equal(
|
||||
'<mermaid displayMode="split" source="flowchart TB\nA --> B\nB --> C">' +
|
||||
'</mermaid>'
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'works correctly when empty', () => {
|
||||
editor.setData(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid"></code>' +
|
||||
'</pre>'
|
||||
);
|
||||
|
||||
expect( getModelData( model, { withoutSelection: true } ) ).to.equal(
|
||||
'<mermaid displayMode="split" source=""></mermaid>'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'data downcast', () => {
|
||||
it( 'works correctly', () => {
|
||||
// Using editor.setData() instead of setModelData helper because of #11365.
|
||||
editor.setData(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid">flowchart TB\nA --> B\nB --> C</code>' +
|
||||
'</pre>'
|
||||
);
|
||||
|
||||
expect( editor.getData() ).to.equal(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid">flowchart TB\nA --> B\nB --> C</code>' +
|
||||
'</pre>'
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'works correctly when empty ', () => {
|
||||
// Using editor.setData() instead of setModelData helper because of #11365.
|
||||
editor.setData(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid"></code>' +
|
||||
'</pre>'
|
||||
);
|
||||
|
||||
expect( editor.getData() ).to.equal(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid"></code>' +
|
||||
'</pre>'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'editing downcast', () => {
|
||||
it( 'works correctly without displayMode attribute', () => {
|
||||
// Using editor.setData() instead of setModelData helper because of #11365.
|
||||
editor.setData(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid">flowchart TB\nA --> B\nB --> C</code>' +
|
||||
'</pre>'
|
||||
);
|
||||
|
||||
expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal(
|
||||
'<div class="ck-mermaid__split-mode ck-mermaid__wrapper ck-widget ck-widget_selected' +
|
||||
' ck-widget_with-selection-handle" contenteditable="false">' +
|
||||
'<div class="ck ck-widget__selection-handle"></div>' +
|
||||
// New lines replaced with space, same issue in getViewData as in #11365.
|
||||
'<textarea class="ck-mermaid__editing-view" data-cke-ignore-events="true"' +
|
||||
' placeholder="Insert mermaid source code"></textarea>' +
|
||||
'<div class="ck-mermaid__preview"></div>' +
|
||||
'<div class="ck ck-reset_all ck-widget__type-around"></div>' +
|
||||
'</div>'
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'works correctly with displayMode attribute', () => {
|
||||
setModelData( editor.model,
|
||||
'<mermaid source="foo" displayMode="preview"></mermaid>'
|
||||
);
|
||||
|
||||
expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal(
|
||||
'<div class="ck-mermaid__preview-mode ck-mermaid__wrapper ck-widget ck-widget_selected ' +
|
||||
'ck-widget_with-selection-handle" contenteditable="false">' +
|
||||
'<div class="ck ck-widget__selection-handle"></div>' +
|
||||
'<textarea class="ck-mermaid__editing-view" data-cke-ignore-events="true"' +
|
||||
' placeholder="Insert mermaid source code"></textarea>' +
|
||||
'<div class="ck-mermaid__preview"></div>' +
|
||||
'<div class="ck ck-reset_all ck-widget__type-around"></div>' +
|
||||
'</div>'
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'works correctly with empty source', () => {
|
||||
setModelData( editor.model,
|
||||
'<mermaid source="" displayMode="preview"></mermaid>'
|
||||
);
|
||||
|
||||
expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal(
|
||||
'<div class="ck-mermaid__preview-mode ck-mermaid__wrapper ck-widget ck-widget_selected ' +
|
||||
'ck-widget_with-selection-handle" contenteditable="false">' +
|
||||
'<div class="ck ck-widget__selection-handle"></div>' +
|
||||
'<textarea class="ck-mermaid__editing-view" data-cke-ignore-events="true"' +
|
||||
' placeholder="Insert mermaid source code"></textarea>' +
|
||||
'<div class="ck-mermaid__preview"></div>' +
|
||||
'<div class="ck ck-reset_all ck-widget__type-around"></div>' +
|
||||
'</div>'
|
||||
);
|
||||
} );
|
||||
|
||||
describe( 'textarea value', () => {
|
||||
let domTextarea = null;
|
||||
|
||||
beforeEach( () => {
|
||||
// Using editor.setData() instead of setModelData helper because of #11365.
|
||||
editor.setData(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid">flowchart TB\nA --> B\nB --> C</code>' +
|
||||
'</pre>'
|
||||
);
|
||||
|
||||
const textareaView = editor.editing.view.document.getRoot().getChild( 0 ).getChild( 1 );
|
||||
domTextarea = editor.editing.view.domConverter.viewToDom( textareaView );
|
||||
} );
|
||||
|
||||
it( 'is properly set during the initial conversion', () => {
|
||||
expect( domTextarea.value ).to.equal( 'flowchart TB\nA --> B\nB --> C' );
|
||||
} );
|
||||
|
||||
it( 'is properly updated after model\'s attribute change', () => {
|
||||
const { model } = editor;
|
||||
|
||||
const mermaidModel = model.document.getRoot().getChild( 0 );
|
||||
|
||||
model.change( writer => {
|
||||
writer.setAttribute( 'source', 'abc', mermaidModel );
|
||||
} );
|
||||
|
||||
expect( domTextarea.value ).to.equal( 'abc' );
|
||||
} );
|
||||
|
||||
it( 'doesn\'t loop if model attribute changes to the same value', () => {
|
||||
const { model } = editor;
|
||||
|
||||
const mermaidModel = model.document.getRoot().getChild( 0 );
|
||||
|
||||
model.change( writer => {
|
||||
writer.setAttribute( 'source', 'flowchart TB\nA --> B\nB --> C', mermaidModel );
|
||||
} );
|
||||
|
||||
expect( domTextarea.value ).to.equal( 'flowchart TB\nA --> B\nB --> C' );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'preview div', () => {
|
||||
let domPreviewContainer, renderMermaidStub;
|
||||
|
||||
beforeEach( () => {
|
||||
// Using editor.setData() instead of setModelData helper because of #11365.
|
||||
editor.setData(
|
||||
'<pre spellcheck="false">' +
|
||||
'<code class="language-mermaid">flowchart TB\nA --> B\nB --> C</code>' +
|
||||
'</pre>'
|
||||
);
|
||||
|
||||
const previewContainerView = editor.editing.view.document.getRoot().getChild( 0 ).getChild( 2 );
|
||||
domPreviewContainer = editor.editing.view.domConverter.viewToDom( previewContainerView );
|
||||
|
||||
renderMermaidStub = sinon.stub( editor.plugins.get( 'MermaidEditing' ), '_renderMermaid' );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
renderMermaidStub.restore();
|
||||
} );
|
||||
|
||||
it( 'has proper inner text set during the initial conversion', () => {
|
||||
expect( domPreviewContainer.textContent ).to.equal( 'flowchart TB\nA --> B\nB --> C' );
|
||||
} );
|
||||
|
||||
it( 'has proper inner text set after a model\'s attribute change', () => {
|
||||
const { model } = editor;
|
||||
|
||||
const mermaidModel = model.document.getRoot().getChild( 0 );
|
||||
|
||||
model.change( writer => {
|
||||
writer.setAttribute( 'source', 'abc', mermaidModel );
|
||||
} );
|
||||
|
||||
expect( domPreviewContainer.textContent ).to.equal( 'abc' );
|
||||
} );
|
||||
|
||||
it( 'calls mermaid render function after a model\'s attribute change', () => {
|
||||
const { model } = editor;
|
||||
|
||||
const mermaidModel = model.document.getRoot().getChild( 0 );
|
||||
|
||||
model.change( writer => {
|
||||
writer.setAttribute( 'source', 'abc', mermaidModel );
|
||||
} );
|
||||
|
||||
expect( renderMermaidStub.callCount ).to.equal( 1 );
|
||||
sinon.assert.calledWithExactly( renderMermaidStub, domPreviewContainer );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'adds a editing pipeline converter that has a precedence over code block', () => {
|
||||
setModelData( editor.model, '<mermaid source="foo"></mermaid>' );
|
||||
|
||||
const firstViewChild = editor.editing.view.document.getRoot().getChild( 0 );
|
||||
|
||||
expect( firstViewChild.name ).to.equal( 'div' );
|
||||
expect( firstViewChild.hasClass( 'ck-mermaid__wrapper' ), 'has ck-mermaid__wrapper class' ).to.be.true;
|
||||
} );
|
||||
|
||||
it( 'does not convert code blocks other than mermaid language', () => {
|
||||
setModelData( editor.model, '<codeBlock language="javascript">foo</codeBlock>' );
|
||||
|
||||
const firstViewChild = editor.editing.view.document.getRoot().getChild( 0 );
|
||||
|
||||
expect( firstViewChild.name ).not.to.equal( 'div' );
|
||||
expect( firstViewChild.hasClass( 'ck-mermaid__wrapper' ), 'has ck-mermaid__wrapper class' ).to.be.false;
|
||||
} );
|
||||
|
||||
it( 'adds a preview element', () => {
|
||||
setModelData( editor.model, '<mermaid source="foo"></mermaid>' );
|
||||
|
||||
const widgetChildren = [ ...editor.editing.view.document.getRoot().getChild( 0 ).getChildren() ];
|
||||
const previewView = widgetChildren.filter( item => item.name === 'div' && item.hasClass( 'ck-mermaid__preview' ) );
|
||||
|
||||
expect( previewView.length ).to.equal( 1 );
|
||||
} );
|
||||
|
||||
it( 'adds an editing element', () => {
|
||||
setModelData( editor.model, '<mermaid source="foo"></mermaid>' );
|
||||
|
||||
const widgetChildren = [ ...editor.editing.view.document.getRoot().getChild( 0 ).getChildren() ];
|
||||
const previewView = widgetChildren.filter(
|
||||
item => item.name === 'textarea' && item.hasClass( 'ck-mermaid__editing-view' )
|
||||
);
|
||||
|
||||
expect( previewView.length ).to.equal( 1 );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
154
packages/ckeditor5-mermaid/tests/mermaidtoolbar.js
Normal file
154
packages/ckeditor5-mermaid/tests/mermaidtoolbar.js
Normal file
@@ -0,0 +1,154 @@
|
||||
import { ClassicEditor as ClassicTestEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { Essentials } from '@ckeditor/ckeditor5-essentials';
|
||||
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
|
||||
import { WidgetToolbarRepository } from '@ckeditor/ckeditor5-widget';
|
||||
import { _setModelData as setData } from '@ckeditor/ckeditor5-engine';
|
||||
|
||||
import Mermaid from '../src/mermaid.js';
|
||||
|
||||
/* global document */
|
||||
|
||||
describe( 'MermaidToolbar', () => {
|
||||
let editor, domElement, widgetToolbarRepository, balloon, toolbar, model;
|
||||
|
||||
beforeEach( () => {
|
||||
domElement = document.createElement( 'div' );
|
||||
document.body.appendChild( domElement );
|
||||
|
||||
return ClassicTestEditor.create( domElement, {
|
||||
plugins: [ Essentials, Paragraph, Mermaid ],
|
||||
mermaid: {
|
||||
toolbar: [ 'fake_button' ]
|
||||
}
|
||||
} ).then( newEditor => {
|
||||
editor = newEditor;
|
||||
model = newEditor.model;
|
||||
widgetToolbarRepository = editor.plugins.get( WidgetToolbarRepository );
|
||||
toolbar = widgetToolbarRepository._toolbarDefinitions.get( 'mermaidToolbar' ).view;
|
||||
balloon = editor.plugins.get( 'ContextualBalloon' );
|
||||
} );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
domElement.remove();
|
||||
return editor.destroy();
|
||||
} );
|
||||
|
||||
describe( 'toolbar', () => {
|
||||
it( 'should be initialized with expected buttons', () => {
|
||||
editor.ui.focusTracker.isFocused = true;
|
||||
|
||||
setData( model, '<mermaid displayMode="split" source="">[]</mermaid>' );
|
||||
|
||||
expect( toolbar.items ).to.have.length( 5 );
|
||||
expect( toolbar.items.get( 0 ).label ).to.equal( 'Source view' );
|
||||
expect( toolbar.items.get( 1 ).label ).to.equal( 'Split view' );
|
||||
expect( toolbar.items.get( 2 ).label ).to.equal( 'Preview' );
|
||||
expect( toolbar.items.get( 4 ).label ).to.equal( 'Read more about Mermaid diagram syntax' );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'integration with the editor focus', () => {
|
||||
it( 'should show the toolbar when the editor gains focus and the mermaid widget is selected', () => {
|
||||
setData( model,
|
||||
'[<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]'
|
||||
);
|
||||
|
||||
expect( balloon.visibleView ).to.be.null;
|
||||
// @todo: remove me
|
||||
// expect( balloon.visibleView === null, 'balloon.visibleView === null' ).to.be.true;
|
||||
|
||||
editor.ui.focusTracker.isFocused = true;
|
||||
expect( balloon.visibleView ).to.equal( toolbar );
|
||||
// @todo: remove me
|
||||
// expect( balloon.visibleView === toolbar, 'balloon.visibleView === toolbar' ).to.be.true;
|
||||
} );
|
||||
|
||||
it( 'should hide the toolbar when the editor loses focus and the mermaid widget is selected', () => {
|
||||
setData( model,
|
||||
'[<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]'
|
||||
);
|
||||
|
||||
editor.ui.focusTracker.isFocused = true;
|
||||
expect( balloon.visibleView ).to.equal( toolbar );
|
||||
|
||||
editor.ui.focusTracker.isFocused = false;
|
||||
expect( balloon.visibleView ).to.be.null;
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'integration with the editor selection', () => {
|
||||
beforeEach( () => {
|
||||
editor.ui.focusTracker.isFocused = true;
|
||||
} );
|
||||
|
||||
it( 'should show the toolbar on ui#update when the mermaid widget is selected', () => {
|
||||
setData( model,
|
||||
'<paragraph>[foo]</paragraph>' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>'
|
||||
);
|
||||
|
||||
expect( balloon.visibleView ).to.be.null;
|
||||
|
||||
editor.ui.fire( 'update' );
|
||||
|
||||
expect( balloon.visibleView ).to.be.null;
|
||||
|
||||
model.change( writer => {
|
||||
// Set selection to the [<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]
|
||||
writer.setSelection( model.document.getRoot().getChild( 1 ), 'on' );
|
||||
} );
|
||||
|
||||
expect( balloon.visibleView ).to.equal( toolbar );
|
||||
|
||||
// Make sure successive change does not throw, e.g. attempting
|
||||
// to insert the toolbar twice.
|
||||
editor.ui.fire( 'update' );
|
||||
|
||||
expect( balloon.visibleView ).to.equal( toolbar );
|
||||
} );
|
||||
|
||||
it( 'should hide the toolbar on ui#update if the mermaid widget is de–selected', () => {
|
||||
setData( model,
|
||||
'<paragraph>foo</paragraph>' +
|
||||
'[<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]'
|
||||
);
|
||||
expect( balloon.visibleView ).to.equal( toolbar );
|
||||
|
||||
model.change( writer => {
|
||||
// Select the <paragraph>[foo]</paragraph>
|
||||
writer.setSelection( model.document.getRoot().getChild( 0 ), 'in' );
|
||||
} );
|
||||
|
||||
expect( balloon.visibleView ).to.be.null;
|
||||
|
||||
// Make sure successive change does not throw, e.g. attempting
|
||||
// to remove the toolbar twice.
|
||||
editor.ui.fire( 'update' );
|
||||
|
||||
expect( balloon.visibleView ).to.be.null;
|
||||
} );
|
||||
|
||||
it( 'should not hide the toolbar on ui#update when the selection is being moved from one mermaid widget to another', () => {
|
||||
setData( model,
|
||||
'[<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>]' +
|
||||
'<mermaid source="flowchart TB\nA --> B\nB --> C"></mermaid>'
|
||||
);
|
||||
|
||||
expect( balloon.visibleView ).to.equal( toolbar );
|
||||
|
||||
model.change( writer => {
|
||||
// Set selection to the second <mermaid></mermaid>
|
||||
writer.setSelection( model.document.selection.getSelectedElement().nextSibling, 'on' );
|
||||
} );
|
||||
|
||||
expect( balloon.visibleView ).to.equal( toolbar );
|
||||
|
||||
// Make sure successive change does not throw, e.g. attempting
|
||||
// to insert the toolbar twice.
|
||||
editor.ui.fire( 'update' );
|
||||
|
||||
expect( balloon.visibleView ).to.equal( toolbar );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
88
packages/ckeditor5-mermaid/tests/mermaidui.js
Normal file
88
packages/ckeditor5-mermaid/tests/mermaidui.js
Normal file
@@ -0,0 +1,88 @@
|
||||
import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
|
||||
import { _setModelData as setModelData } from '@ckeditor/ckeditor5-engine';
|
||||
|
||||
import Mermaid from '../src/mermaid.js';
|
||||
import MermaidUI from '../src/mermaidui.js';
|
||||
|
||||
/* global document */
|
||||
|
||||
describe( 'MermaidUI', () => {
|
||||
it( 'should be named', () => {
|
||||
expect( MermaidUI.pluginName ).to.equal( 'MermaidUI' );
|
||||
} );
|
||||
|
||||
describe( 'init()', () => {
|
||||
let domElement, editor;
|
||||
|
||||
beforeEach( async () => {
|
||||
domElement = document.createElement( 'div' );
|
||||
document.body.appendChild( domElement );
|
||||
|
||||
editor = await ClassicEditor.create( domElement, {
|
||||
plugins: [
|
||||
Mermaid
|
||||
]
|
||||
} );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
domElement.remove();
|
||||
return editor.destroy();
|
||||
} );
|
||||
|
||||
it( 'should register the UI item', () => {
|
||||
expect( editor.ui.componentFactory.has( 'mermaid' ) ).to.equal( true );
|
||||
} );
|
||||
|
||||
it( 'has the base properties', () => {
|
||||
const button = editor.ui.componentFactory.create( 'mermaid' );
|
||||
|
||||
expect( button ).to.have.property( 'label', 'Insert Mermaid diagram' );
|
||||
expect( button ).to.have.property( 'icon' );
|
||||
expect( button ).to.have.property( 'tooltip', true );
|
||||
} );
|
||||
|
||||
describe( 'UI components', () => {
|
||||
for ( const buttonName of [
|
||||
'mermaidPreview',
|
||||
'mermaidSourceView',
|
||||
'mermaidSplitView',
|
||||
'mermaidInfo'
|
||||
] ) {
|
||||
it( `should register the ${ buttonName } button`, () => {
|
||||
expect( editor.ui.componentFactory.has( buttonName ) ).to.equal( true );
|
||||
} );
|
||||
|
||||
it( `should add the base properties for ${ buttonName } button`, () => {
|
||||
const button = editor.ui.componentFactory.create( buttonName );
|
||||
|
||||
expect( button ).to.have.property( 'label' );
|
||||
expect( button ).to.have.property( 'icon' );
|
||||
expect( button ).to.have.property( 'tooltip', true );
|
||||
} );
|
||||
}
|
||||
} );
|
||||
|
||||
it( 'should set focus inside textarea of a newly created mermaid', () => {
|
||||
const button = editor.ui.componentFactory.create( 'mermaid' );
|
||||
|
||||
button.fire( 'execute' );
|
||||
|
||||
expect( document.activeElement.tagName ).to.equal( 'TEXTAREA' );
|
||||
} );
|
||||
|
||||
it( 'should not crash if the button is fired inside model.change()', () => {
|
||||
const button = editor.ui.componentFactory.create( 'mermaid' );
|
||||
|
||||
setModelData( editor.model, '[]' );
|
||||
|
||||
editor.model.change( () => {
|
||||
button.fire( 'execute' );
|
||||
} );
|
||||
// As the conversion is to be executed after the model.change(), we don't have access to the fully prepared view and
|
||||
// despite that, we should still successfully add mermaid widget to the editor, not requiring the selection change
|
||||
// to the inside of the nonexisting textarea element.
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
Reference in New Issue
Block a user