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

Ability to configure split button in config.toolbar property #2094

Closed
wants to merge 20 commits into from

Conversation

engineering-this
Copy link
Contributor

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 feature is rather straight forward. I've selected base as t/1679 so GH displays real diff for this branch.

Closes #2091

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.

The direction is good, and it works pretty well.

But the biggest concern now is that due to that change, now the API becomes less consistent. I'm talking about default option here.

I don't like the fact that we have more than one way to set the default action of the Split Button, that is:

  • set the default action name directly in Split Button configuration object
  • set the default action name Split Button's items, by adding default: true

It seems to me that the first option (having it directly in splitbutton definition, single prop) is better, and should be the only one to remain.

@@ -535,6 +535,12 @@

// Ignore items that are configured to be removed.
if ( !removeButtons || CKEDITOR.tools.indexOf( removeButtons, name ) == -1 ) {
// Option to define Split Button or other UI elements in `config.toolbar` #2091
if ( item.type ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please simplify 2 subsequent ifs to just a single if ( cond1 && cond2 ){}.

@@ -535,6 +535,12 @@

// Ignore items that are configured to be removed.
if ( !removeButtons || CKEDITOR.tools.indexOf( removeButtons, name ) == -1 ) {
// Option to define Split Button or other UI elements in `config.toolbar` #2091
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 only limited to Split Button UI element alone. Similarly you can now create regular buttons, dropdowns etc. So adjust the docs so that it does refer to known UI types.

Also when doing inline docs follow the regular format, that is:

  • place ticket reference in parenthesis,
  • end the sentence with a dot.

See the following as an example: https://github.com/ckeditor/ckeditor-dev/blob/ecfbb1e/plugins/stylescombo/plugin.js#L145

@@ -0,0 +1,11 @@
@bender-ui: collapsed
@bender-tags: feature, 4.10.0, 1679
Copy link
Contributor

Choose a reason for hiding this comment

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

Ticket in tag should refer to this particular feature issue id, rather than the whole Split Button feature.

@@ -34,7 +43,21 @@
}

for ( i in allItems ) {
item = allItems[ i ];
itemName = item = allItems[ i ];
Copy link
Contributor

Choose a reason for hiding this comment

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

You're using itemName in just a single place, that is:

https://github.com/ckeditor/ckeditor-dev/blob/ecfbb1e/plugins/splitbutton/plugin.js#L56

You don't have to create a temp variable for the name to access it there, the name is still available at item.name. Use it, and remove itemName - this will reduce the complexity.

previousButton,
defaultButton;

if ( face ) {
// Split Button can be defined with simpler definition via strings, needed by #2091
Copy link
Contributor

Choose a reason for hiding this comment

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

Format should be adjusted same as before. This also occurs later on in this PR, check for other occurrences.

previousButton,
defaultButton;

if ( face ) {
// Split Button can be defined with simpler definition via strings, needed by #2091
if ( typeof face === 'string' ) {
face = editor.ui.items[ face ];
Copy link
Contributor

Choose a reason for hiding this comment

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

You assigning an object that you modify few lines below. That means you're modifying referenced icon.

We can not do that, you should clone it first and work on it clone instead.

items: items
} );

editor.ui.add( 'customclick', CKEDITOR.UI_SPLITBUTTON, {
Copy link
Contributor

Choose a reason for hiding this comment

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

Code registered as customclick handler is duplicated, extract it to common parts.

face: {
icon: 'underline',
click: function() {
counter.faceClick++;
Copy link
Contributor

Choose a reason for hiding this comment

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

The whole counter concept could be simplified by using sinon's stubs. Since you're using them in tests, you could just define stubs in test-scope or in context of tests object.

Then you'd want to reset it in setup/teardown function to make sure that test can not be affected by other unit tests.

Copy link
Contributor Author

@engineering-this engineering-this Jun 18, 2018

Choose a reason for hiding this comment

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

Refactor of tests/plugins/splitbutton/splitbutton.js is extracted to #2098

@engineering-this
Copy link
Contributor Author

I've pushed to this branch commit form #2098.

@mlewand mlewand self-requested a review June 20, 2018 06:23
face = CKEDITOR.tools.clone( editor.ui.items[ face ] );

if ( !face.icon ) {
face.icon = face.name || face.command;
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the use case for this? If there is no icon in the original button it's fine to leave it without an icon.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually this one can be removed, but next case is required, explanations below.

item = CKEDITOR.tools.clone( editor.ui.items[ item ] );

if ( !item.icon ) {
item.icon = item.name || item.command;
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar case as before.

Copy link
Contributor Author

@engineering-this engineering-this Jun 20, 2018

Choose a reason for hiding this comment

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

That is how button plugin works.
https://github.com/ckeditor/ckeditor-dev/blob/6c0469b530cbc767946379e00b5aedcb01e2ba5a/plugins/button/plugin.js#L262-L266

If there is no buttonDefinition.icon property, then icon will fallback to name or command. But menu plugins doesn't do that, so in order for menu item to have an icon we need to find that icon same way as button plugin does.

Example opening ~/workspace/ckeditor/ckeditor-dev/samples/index.html and executing following in console:

CKEDITOR.instances.editor.ui.get( 'Bold' );

Returned object will not have any icon property. Actually when writing this I'm thinking if maybe we should change how button works, so it always have icon property when there is icon on button. WDYT @mlewand?

Copy link
Contributor

Choose a reason for hiding this comment

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

As for assigning the icon property during button creation - this sounds reasonable if only we started with such approach. Now there might be 3rd party integrations that rely on this property, and rely on it being a "falsy" value. So such change could break some solutions, we don't want to break things.

This rationale sounds reasonable. In this case it make sense to revert this fallback, thanks for clarifying.

Please just drop a short note, or a link to your comment here.

Also make sure to convert the code link above to a perma link, so something like that: https://github.com/ckeditor/ckeditor-dev/blob/6c0469b530cbc767946379e00b5aedcb01e2ba5a/plugins/button/plugin.js#L262-L263

@@ -94,12 +115,12 @@

editor.addFeature( properties.buttons[ item.id ] );

// First button as default. It might be overwritten by actual default button.
if ( !defaultButton && !item[ 'default' ] ) {
// If no default button was defined then first button will act as default.
Copy link
Contributor

Choose a reason for hiding this comment

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

This logic could be now simplified. Here, in the loop, you could just leave if ( definition[ 'default' ] && definition[ 'default' ] === item.name ) { check over here.

Then the second if should go outside the loop, and that would check for conditions:

  • whether defaultButton was not picked, with the logic above ☝️
  • whether definition[ 'default' ] was not defined at all

Then assign the first item as a default button (if there was at least one item).

previousButton = properties.buttons[ item.id ];
defaultButton = properties.buttons[ item.id ];
} else {
if ( item[ 'default' ] ) {
if ( definition[ 'default' ] && definition[ 'default' ].toLowerCase() === item.name ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

definition.default should be given as a button name, thus it should not be case-sensitive. We expect name Bold, Image etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is case when we define split button in short forms with items as strings.

To get an item via ui.get we need to pass an uppercased name eg. Bold. But this uppercased name exists only in ui.items / ui.instances as a key.
Object returned via ui.get has property name lowercased.

Example:
~/workspace/ckeditor/ckeditor-dev/samples/index.html

var bold = CKEDITOR.instances.editor.ui.get( 'bold' );
console.log( bold ); // undefined
bold = CKEDITOR.instances.editor.ui.get( 'Bold' );
console.log( bold.name ) // 'bold'

@mlewand mlewand self-requested a review July 16, 2018 09:45
@@ -448,9 +448,9 @@
* @param {String} definition.command The command to be executed once the button is activated.
* @param {String} definition.toolbar The {@link CKEDITOR.config#toolbarGroups toolbar group} into which
* the button will be added. An optional index value (separated by a comma) determines the button position within the group.
* @param {String} definition.style The optional inline style that will be applied on button. Custom button styles are supported sins the **4.10.0** version.
* @param {String} definition.style The optional inline style that will be applied on button. Custom button styles are supported sins the **4.11.0** version.
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo in "are supported sins the"

@@ -535,6 +535,10 @@

// Ignore items that are configured to be removed.
if ( !removeButtons || CKEDITOR.tools.indexOf( removeButtons, name ) == -1 ) {
// Option to define UI element in `config.toolbar` (#2091).
if ( item.type && item.type in editor.ui._.handlers ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This feature of supporting type property is nowhere to be found in the API docs. It needs to be reflected in API docs.

@@ -1,5 +1,5 @@
@bender-ui: collapsed
@bender-tags: feature, 4.10.0, 1679
@bender-tags: feature, 4.11.0, 1679
Copy link
Contributor

Choose a reason for hiding this comment

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

Technically this change is out of scope of this issue. It would be nice if you could extract this commit from history and move it to a proper feature branch - but it's not a big deal if it will remain here in case it's tough to do so.

item = CKEDITOR.tools.clone( editor.ui.items[ item ] );

if ( !item.icon ) {
item.icon = item.name || item.command;
Copy link
Contributor

Choose a reason for hiding this comment

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

As for assigning the icon property during button creation - this sounds reasonable if only we started with such approach. Now there might be 3rd party integrations that rely on this property, and rely on it being a "falsy" value. So such change could break some solutions, we don't want to break things.

This rationale sounds reasonable. In this case it make sense to revert this fallback, thanks for clarifying.

Please just drop a short note, or a link to your comment here.

Also make sure to convert the code link above to a perma link, so something like that: https://github.com/ckeditor/ckeditor-dev/blob/6c0469b530cbc767946379e00b5aedcb01e2ba5a/plugins/button/plugin.js#L262-L263

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.

The code breaks if you set default with an invalid string. Such cases could happen and it should not break the editor.

For instance:

Open https://github.com/ckeditor/ckeditor-dev/blob/b2cb7cc/tests/plugins/splitbutton/manual/toolbar.html#L9-L24 and use the following snippet:

	CKEDITOR.replace( 'editor', {
		toolbar: [ [ {
			type: 'splitbutton',
			name: 'BasicStylesSplitButton',
			label: 'Basic styles',
			face: 'Bold',
			items: [ 'Bold' , 'Italic', 'Underline' ]
		} ], [ {
			type: 'splitbutton',
			name: 'JustifySplitButton',
			label: 'Justify',
			items: [ 'JustifyLeft' , 'JustifyCenter', 'JustifyRight', 'JustifyBlock' ],
			'default': 'JustifyRight123'
			} ]
		]
	} );

Of course it must be covered by a unit test.

@engineering-this
Copy link
Contributor Author

Rebased onto updated #1679 branch. See comment.
Also dropped commits updating version number, as it is now corrected in feature branch.

@engineering-this
Copy link
Contributor Author

Rebased, as parent branch was rebased also.

@engineering-this engineering-this removed their assignment May 30, 2019
@f1ames f1ames closed this Mar 20, 2020
@f1ames f1ames added the pr:frozen ❄ The pull request on which work was suspended due to various reasons. label Mar 20, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr:frozen ❄ The pull request on which work was suspended due to various reasons.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants