Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduced mentions plugin #1932

Merged
merged 56 commits into from
Jun 20, 2018
Merged

Introduced mentions plugin #1932

merged 56 commits into from
Jun 20, 2018

Conversation

jacekbogdanski
Copy link
Member

@jacekbogdanski jacekbogdanski commented Apr 27, 2018

What is the purpose of this pull request?

New feature

Does your PR contain necessary tests?

All patches which change the editor code must include tests. You can always read more
on PR testing,
how to set the testing environment and
how to create tests
in the official CKEditor documentation.

This PR contains

  • Unit tests
  • Manual tests

What changes did you make?

This PR continues work on #1703 plugin. See #1757 for more details.

Closes #1703, #2101

@mlewand mlewand self-requested a review May 9, 2018 09:01
Copy link
Contributor

@mlewand mlewand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far just a couple of docs related issues.

Most importantly we need better docs for config.mentions option. There should be an abstract type like CKEDITOR.plugins.mentions.configDefinition that would document config option.

Then config.mentions should be set as a CKEDITOR.plugins.mentions.configDefinition[] type. Also config parameter of CKEDITOR.plugin.mentions type should be marked as CKEDITOR.plugins.mentions.configDefinition.

'use strict';

( function() {
var MARKER = '@', MIN_CHARS = 2, ENCODED_QUERY = '{encodedQuery}';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not define multiple vars in the same line.

this.minChars = config.minChars !== null && config.minChars !== undefined ? config.minChars : MIN_CHARS;

/**
* {@link CKEDITOR.plugins.autocomplete Autocomplete} instance used by mentions feature to implement autocompletion logic.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a private property.

* @since 4.10.0
* @constructor Creates the new instance of mentions and attaches it to the editor using {@link CKEDITOR.plugins.autocomplete Autocomplete feature}.
* @param {CKEDITOR.editor} editor The editor to watch.
* @param {Object} config Configuration object keeping information how to instantiate mentions plugin.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This parameter should use abstract type definition, for an example, it could be named CKEDITOR.plugins.mentions.configDefinition.

* A character that should trigger autocompletion.
* @property {String} [marker='@']
*/
this.marker = config.marker || MARKER;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This property is not meant to be modified. Thus it should be marked as readonly.

* A number of characters that should follow the marker character in order to trigger mentions feature.
* @property {Number} [minChars=2]
*/
this.minChars = config.minChars !== null && config.minChars !== undefined ? config.minChars : MIN_CHARS;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as this.marker (readonly).


/**
* A character that should trigger autocompletion.
* @property {String} [marker='@']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, leave consistently one line between property/method description and API doc tags section. This issue appears in other places too.

this.marker = config.marker || MARKER;

/**
* A number of characters that should follow the marker character in order to trigger mentions feature.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"marker character" should be linked, so for instance {@link #marker marker character}.

*
* @class CKEDITOR.plugins.mentions
* @since 4.10.0
* @constructor Creates the new instance of mentions and attaches it to the editor using {@link CKEDITOR.plugins.autocomplete Autocomplete feature}.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"using Autocomplete feature" is not needed here. It should be simply "to the editor.", period. The fact that it utilizes autocomplete under the hood is not important for docs reader at this level.

getTextTestCallback( this.marker, this.minChars ),
getDataCallback( feed, this.marker, this.caseSensitive ) );

template && this.changeViewTemplate( template );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not use x && y() form. Use a regular ifs.

},

/**
* Changes the view template. The given template will be used by {@link CKEDITOR.template}.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The given template will be used by {@link CKEDITOR.template}.

I don't think this has value. Focus on the purpose of method, rather on how something is done.

@mlewand
Copy link
Contributor

mlewand commented May 11, 2018

During the review I noticed a major requirement that is needed before deploying the feature:

Copy link
Contributor

@mlewand mlewand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have extracted issue related to caching to a separate issue so that it's easier to track.

There's also an issue with possible race condition. Also I noted multiple things related to docs. I want to stress that the docs are vital for this feature. Also it's about time we start to work on sdk sample and docs in paraler to this feature (ckeditor/ckeditor4-sdk#240).

The proposed solution is prone to response race conditions.

A scenario where server responses does not come in exact order, as they were issued, is pretty possible - wile easy to avoid. We should consider that too.

Imagine that the user opens CKE with "@ann" as a content and wants to type "@annaB" in order to use "Annabelle" suggestion.

  1. User starts typing, mentions plugin detects "@anna".
    Server at this point sends "Anna", "Annabelle" response but client did not get it yet! Let's call it response A.
  2. User presses "b" so there's "@annaB" (this is few ms after the previous step).
    Server at this point sends "Annabelle" response but client did not get it yet! Let's call it response B.
  3. User receives response B (note this server response is out of order!).
  4. User receives response A.

Expected

Mention plugin shows only "Annabelle" option.

Actual

Mention plugin shows both "Anna" and "Annabelle" options.

/**
* A character that should trigger autocompletion.
*
* @property {String} [marker='@'] marker
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a valid @property tag syntax. What's more is that it leaks artefacts to the rendered:

image

} );

/**
* Mentions plugin allows you to type custom marker character and get suggested values for the usernames so that you don't have to write it on your own.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the usernames so that

This functionallity is not limited to user names. It could reference just anything.

* Mentions plugin allows you to type custom marker character and get suggested values for the usernames so that you don't have to write it on your own.
*
* The recommended way to add mentions feature to an editor is by
* using {@link CKEDITOR.config#mentions} configuration property or by passing it as the configuration option when instantiating an editor.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a better inner text would be config.mentions also "configuration" word is redundant.

See how it looks currently:

(…) using CKEDITOR.config.mentions configuration property (…)

What I'm proposing is something more like

(…) using config.mentions property (…)

*
* ```javascript
* // Simple usage with CKEDITOR.config.mentions property.
* CKEDITOR.config.mentions = [ { feed: ['Anna', 'Thomas', 'John'], minChars: 0, marker: '#' } ];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's skip marker property customization here. minChars is fine.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this is not an optimal way to change this configuration.

This code will most likely not work when the plugin is loaded dynamically.

Imagine you have website code like:

<script src="ckeditor/4.9.2/ckeditor.js"></script>
<script>
	CKEDITOR.config.mentions = [ { feed: ['Anna', 'Thomas', 'John'], minChars: 0, marker: '#' } ];

	CKEDITOR.replace( 'editable', {
		extraPlugins: mentions
	} );
</script>

Your config will get overridden. Why is that? Because plugin.js will get loaded asynchronously. Meaning that your default value will kick in after the value above was assigned.

Please update other places accordingly. I think in this case you need to refer to config.mentions = [ (…) ]. In case of doubts contact our docs manager.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case shouldn't we remove default CKEDITOR.config.mentions = [] initialization so it won't overwrite user configuration?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, that's fine.

*
* // Passing mentions configuration when creating editor.
* CKEDITOR.replace( 'editor', {
* mentions: [ { feed: ['Anna', 'Thomas', 'John'], minChars: 0, marker: '#' } ]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above:

Let's skip marker property customization here. minChars is fine.


callback = getDataCallbackWithMarkedItems( marker, callback );

// Feed is a simple array e.g. [ 'Anna', 'John', 'Thomas' ].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocks:

// Feed is a simple array e.g. [ 'Anna', 'John', 'Thomas' ].

// Feed is a URL to be requested for a JSON of matches e.g. /user-controller/get-list/{encodedQuery}.

// Feed is a function which uses callback to pass data through autocomplete plugin e.g.

Should be palced in feed property API docs rather than here in the code.

*/

/**
* Feed of items to be displayed in mentions plugin.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This property is not properly documented. It's extremely important property, while this its entire doc:

image

You have placed a lot of it in mentions.configDefinition type description. I feel that type definition should be very short and simple.

It make sense to add one listingbut that's about it. From there it should reference feed property for further reading. I think it will be much better path.

/**
* Template used to render matches in the dropdown.
*
* @property {String} [template=CKEDITOR.plugins.autocomplete.view.itemTemplate] template
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What attributes does it (the template) take? At least code listing would be valuable here.

var encodedQueryRegex = new RegExp( ENCODED_QUERY, 'g' ),
encodedUrl = feed.replace( encodedQueryRegex, encodeURIComponent( query ) );

CKEDITOR.ajax.load( encodedUrl, function( data ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code doesn't handle response race conditions. I'll inlcude the full repro case in review summary.


// Feed is a URL to be requested for a JSON of matches e.g. `/user-controller/get-list/{encodedQuery}`.
else if ( typeof feed === 'string' ) {
var encodedQueryRegex = new RegExp( ENCODED_QUERY, 'g' ),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please reuse CKEDITOR.template type for replacement logic.

@jacekbogdanski
Copy link
Member Author

The issue #1932 (review) has been handled inside autocomplete plugin. Although I will create some additional tests to be sure it won't be accidentally broken by future autocomplete plugin changes.

@jacekbogdanski
Copy link
Member Author

Rebased into t/1751.

@jacekbogdanski
Copy link
Member Author

Rebased into t/1751.

@jacekbogdanski jacekbogdanski force-pushed the t/1703-b branch 2 times, most recently from 7f26536 to d143232 Compare June 11, 2018 08:27
@jacekbogdanski jacekbogdanski force-pushed the t/1703-b branch 2 times, most recently from e8c25fa to b457315 Compare June 19, 2018 09:56
@jacekbogdanski
Copy link
Member Author

I rebased this PR into the latest upstream. I also referenced #2101 issue.

@jacekbogdanski jacekbogdanski changed the base branch from t/1751 to major June 19, 2018 12:29
Copy link
Contributor

@mlewand mlewand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests are running and failing on IE8.


1. Focus the editor.
1. Type `@anna`.
1. Press enter.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to add a point to type a space, because without space, if you start to type @anna@anna or anything else, text the text will not issue the request anyway.


## Expected

History log doesn't contain duplicated URL requests.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

URL requests - we should simplify it, as the tester does not see URLs but simply something like:

Request: ""
Request: "a"
Request: "an"
Request: "anna"
Request: "annaJ"
Request: "j"
Request: "joh"
Request: "john"

Let's refer to it just as request.

@@ -0,0 +1,10 @@
@bender-tags: mentions, feature, 4.10.0, 1703
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This manual test deserves to become a generic manual test (so that it gets tested during each future release).

To do that you need to remove feature feature/bug branch.

*
* ```javascript
* // Simple usage with CKEDITOR.config.mentions property.
* CKEDITOR.config.mentions = [ { feed: ['Anna', 'Thomas', 'John'], minChars: 0 } ];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommended way is to set it up by passing the config directly to the editor instance, global config, although valid, is not a common method in CKE4. So I'd switch these two.

return function( query, range, callback ) {
// We are removing marker here to give clean query result for the endpoint callback.
if ( mentions.marker ) {
query = query.substring( 1 );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The length of a marker should be dynamically computed, so mentions.marker.length instead of magic number 1

@mlewand
Copy link
Contributor

mlewand commented Jun 20, 2018

I have pushed a commit fixing most of the above issues, except IE8 thing. I don't see more issues than that so please correct it and we're good to go! 👍

@jacekbogdanski jacekbogdanski requested a review from mlewand June 20, 2018 10:26
Copy link
Contributor

@mlewand mlewand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, looks good 👌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants