Skip to content

Commit

Permalink
Added autocomplete templating feature.
Browse files Browse the repository at this point in the history
  • Loading branch information
jacekbogdanski committed May 30, 2018
1 parent cd36772 commit 7211a96
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 5 deletions.
35 changes: 32 additions & 3 deletions plugins/autocomplete/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,10 @@
* @param {CKEDITOR.plugins.autocomplete.model.item[]} dataCallback.callback.data The suggestion data that should be
* displayed in the autocomplete view for a given query. The data items should implement the
* {@link CKEDITOR.plugins.autocomplete.model.item} interface.
* @param {String} [itemTemplate] Template for list item in dropdown. See {@link CKEDITOR.plugins.autocomplete.view#itemTemplate} for more information.
* @param {String} [outputTemplate] Template for match rendering. See {@link #outputTemplate}.
*/
function Autocomplete( editor, textTestCallback, dataCallback ) {
function Autocomplete( editor, textTestCallback, dataCallback, itemTemplate, outputTemplate ) {
var configKeystrokes = editor.config.autocomplete_commitKeystrokes || CKEDITOR.config.autocomplete_commitKeystrokes;

/**
Expand Down Expand Up @@ -211,6 +213,24 @@
*/
this._listeners = [];

/**
* Template of markup to be inserted as the autocomplete item gets committed.
*
* You can use {@link CKEDITOR.plugins.autocomplete.model#data data item} properties to customize the template.
*
* ```javascript
* var outputTemplate = `<a href="/tracker/{ticket}">#{ticket} ({name})</a>`;
* ```
*
* @readonly
* @property {CKEDITOR.template} [outputTemplate=null]
*/
this.outputTemplate = outputTemplate !== undefined ? new CKEDITOR.template( outputTemplate ) : null;

if ( itemTemplate ) {
this.view.itemTemplate = new CKEDITOR.template( itemTemplate );
}

this.attach();
}

Expand Down Expand Up @@ -338,7 +358,7 @@
* @returns {String} The HTML to insert.
*/
getHtmlToInsert: function( item ) {
return item.name;
return this.outputTemplate ? this.outputTemplate.output( item ) : item.name;
},

/**
Expand Down Expand Up @@ -531,7 +551,15 @@
*/
function View( editor ) {
/**
* The panel's item template.
* The panel's item template used to render matches in the dropdown.
*
* You can use {@link CKEDITOR.plugins.autocomplete.model#data data item} properties to customize the template.
*
* A minimal template must be wrapped with HTML `li` element containing `data-id="{id}"` attribute.
*
* ```javascript
* autocomplete.itemTemplate = '<li data-id="{id}"><img src="{iconSrc}" alt="{name}">{name}</li>';
* ```
*
* @readonly
* @property {CKEDITOR.template}
Expand Down Expand Up @@ -1207,4 +1235,5 @@
function iOSViewportElement( editor ) {
return editor.window.getFrame().getParent();
}

} )();
50 changes: 48 additions & 2 deletions tests/plugins/autocomplete/autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
'use strict';

bender.editors = {
standard: {},
standard: {
config: {
extraAllowedContent: 'strong'
}
},
arrayKeystrokes: {
config: {
autocomplete_commitKeystrokes: [ 16 ] // SHIFT
Expand Down Expand Up @@ -272,9 +276,51 @@
assert.areEqual( '50px', ac.view.element.getStyle( 'top' ) );
assert.areEqual( '50px', ac.view.element.getStyle( 'left' ) );

ac.destroy();
},

// (#1987)
'test custom view template': function() {
var editor = this.editors.standard,
itemTemplate = '<li data-id="{id}"><strong>{name}</strong></li>',
ac = new CKEDITOR.plugins.autocomplete( editor, matchTestCallback,
function( query, range, callback ) {
callback( [ { id: 1, name: 'anna' } ] );
},
itemTemplate );

this.editorBots.standard.setHtmlWithSelection( '' );

editor.editable().fire( 'keyup', new CKEDITOR.dom.event( {} ) );

assert.beautified.html( '<ul><li class="cke_autocomplete_selected" data-id="1"><strong>anna</strong></li></ul>',
ac.view.element.getHtml() );

ac.destroy();
},

// (#1987)
'test custom output template': function() {
var editor = this.editors.standard,
editable = editor.editable(),
outputTemplate = '<strong>{name}</strong>',
ac = new CKEDITOR.plugins.autocomplete( editor, matchTestCallback,
function( query, range, callback ) {
callback( [ { id: 1, name: 'anna' } ] );
},
null,
outputTemplate );

this.editorBots.standard.setHtmlWithSelection( '' );

editable.fire( 'keyup', new CKEDITOR.dom.event( {} ) );
editable.fire( 'keydown', new CKEDITOR.dom.event( { keyCode: 13 } ) ); // ENTER

assert.beautified.html( '<p><strong>anna</strong></p>', editable.getData() );

ac.destroy();
}
} );
} );

function assertViewOpened( ac, isOpened ) {
var opened = ac.view.element.hasClass( 'cke_autocomplete_opened' );
Expand Down
38 changes: 38 additions & 0 deletions tests/plugins/autocomplete/manual/outputtemplate.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<div id="editor1" ></div>

<script>
if ( CKEDITOR.env.ie && CKEDITOR.env.version == 8 ) {
bender.ignore();
}

CKEDITOR.replace( 'editor1', {
width: 600,
on: {
instanceReady: function( evt ) {
var template = '<strong>{name}</strong>';
new CKEDITOR.plugins.autocomplete( evt.editor, getTextTestCallback(), dataCallback, null, template );
}
}
} );

function getTextTestCallback() {
return function( range ) {
return CKEDITOR.plugins.textMatch.match( range, matchCallback );
};
}

function matchCallback( text, offset ) {
var left = text.slice( 0, offset ),
match = left.match( new RegExp( '@\\w*$' ) );

if ( !match ) {
return null;
}

return { start: match.index, end: offset };
}

function dataCallback( query, range, callback ) {
callback( [ { id: 1, name: '@anna' } ] );
}
</script>
15 changes: 15 additions & 0 deletions tests/plugins/autocomplete/manual/outputtemplate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@bender-tags: 4.10.0, bug, 1987
@bender-ui: collapsed
@bender-ckeditor-plugins: wysiwygarea, toolbar, basicstyles, autocomplete, textmatch

1. Focus the editor.
1. Type `@`.
1. Wait until dropdown show up and press `enter`.

## Expected

Inserted name is bolded.

## Unexpected

Inserted name is a plain text.
50 changes: 50 additions & 0 deletions tests/plugins/autocomplete/manual/viewtemplate.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<div id="editor1"></div>

<script>
if ( CKEDITOR.env.ie && CKEDITOR.env.version == 8 ) {
bender.ignore();
}

CKEDITOR.replace( 'editor1', {
width: 600,
on: {
instanceReady: function( evt ) {
var template = '<li data-id="{id}">item: <strong>{name}</strong></li>';
new CKEDITOR.plugins.autocomplete( evt.editor, getTextTestCallback(), dataCallback, template );
}
}
} );

function getTextTestCallback() {
return function( range ) {
return CKEDITOR.plugins.textMatch.match( range, matchCallback );
};
}

function matchCallback( text, offset ) {
var left = text.slice( 0, offset ),
match = left.match( new RegExp( '@\\w*$' ) );

if ( !match ) {
return null;
}

return {
start: match.index,
end: offset
};
}

function dataCallback( query, range, callback ) {
callback( [ {
id: 1,
name: 'anna'
},
{
id: 2,
name: 'john'
}
] );
}

</script>
16 changes: 16 additions & 0 deletions tests/plugins/autocomplete/manual/viewtemplate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@bender-tags: 4.10.0, bug, 1987
@bender-ui: collapsed
@bender-ckeditor-plugins: wysiwygarea, toolbar, basicstyles, autocomplete, textmatch

1. Focus the editor.
1. Type `@`.

## Expected

* Dropdown contains **bolded** names.
* All are prefixed with "item: " string.

## Unexpected

* Names inside dropdown are plain text.
* Names are not prefixed with "item: " string.

0 comments on commit 7211a96

Please sign in to comment.