Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Merge pull request #276 from ckeditor/t/269
Browse files Browse the repository at this point in the history
Other: The `ContextualToolbar` should not show up when all child items are disabled. The `ContextualToolbar#beforeShow` event has been replaced by `ContextualToolbar#show`. Closes #269. Closes #232.

BREAKING CHANGE: `ContextualToolbar#beforeShow` is no longer available. Please refer to `ContextualToolbar#show` instead.
  • Loading branch information
oleq authored Jul 18, 2017
2 parents 111a728 + 18e1c6b commit d83d07d
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 40 deletions.
55 changes: 23 additions & 32 deletions src/toolbar/contextual/contextualtoolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ export default class ContextualToolbar extends Plugin {
// Attach lifecycle actions.
this._handleSelectionChange();
this._handleFocusChange();

// The appearance of the ContextualToolbar method is event–driven.
// It is possible to stop the #show event and this prevent the toolbar from showing up.
this.decorate( 'show' );
}

/**
Expand All @@ -95,7 +99,7 @@ export default class ContextualToolbar extends Plugin {
}

/**
* Handles editor focus change and hides panel if it's needed.
* Handles the editor focus change and hides the toolbar if it's needed.
*
* @private
*/
Expand Down Expand Up @@ -146,41 +150,31 @@ export default class ContextualToolbar extends Plugin {
/**
* Shows the toolbar and attaches it to the selection.
*
* Fires {@link #event:beforeShow} event just before displaying the panel.
* Fires {@link #event:show} event which can be stopped to prevent the toolbar from showing up.
*/
show() {
const editingView = this.editor.editing.view;
let isStopped = false;

// Do not add toolbar to the balloon stack twice.
// Do not add the toolbar to the balloon stack twice.
if ( this._balloon.hasView( this.toolbarView ) ) {
return;
}

// If `beforeShow` event is not stopped by any external code then panel will be displayed.
this.once( 'beforeShow', () => {
if ( isStopped ) {
return;
}
// Don not show the toolbar when all components inside are disabled
// see https://github.com/ckeditor/ckeditor5-ui/issues/269.
if ( Array.from( this.toolbarView.items ).every( item => item.isEnabled !== undefined && !item.isEnabled ) ) {
return;
}

// Update panel position when selection changes while balloon will be opened
// (by an external document changes).
this.listenTo( editingView, 'render', () => {
this._balloon.updatePosition( this._getBalloonPositionData() );
} );

// Add panel to the common editor contextual balloon.
this._balloon.add( {
view: this.toolbarView,
position: this._getBalloonPositionData(),
balloonClassName: 'ck-toolbar-container ck-editor-toolbar-container'
} );
// Update the toolbar position upon #render (e.g. external document changes)
// while it's visible.
this.listenTo( this.editor.editing.view, 'render', () => {
this._balloon.updatePosition( this._getBalloonPositionData() );
} );

// Fire this event to inform that `ContextualToolbar` is going to be shown.
// Helper function for preventing the panel from being displayed is passed along with the event.
this.fire( 'beforeShow', () => {
isStopped = true;
// Add the toolbar to the common editor contextual balloon.
this._balloon.add( {
view: this.toolbarView,
position: this._getBalloonPositionData(),
balloonClassName: 'ck-toolbar-container ck-editor-toolbar-container'
} );
}

Expand Down Expand Up @@ -234,12 +228,9 @@ export default class ContextualToolbar extends Plugin {
}

/**
* This event is fired just before the toolbar shows.
* Using this event, an external code can prevent ContextualToolbar
* from being displayed by calling a `stop` function which is passed along with this event.
* This event is fired just before the toolbar shows up. Stopping this event will prevent this.
*
* @event beforeShow
* @param {Function} stop Calling this function prevents panel from being displayed.
* @event show
*/

/**
Expand Down
3 changes: 2 additions & 1 deletion tests/manual/contextualtoolbar/contextualtoolbar.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<div id="editor">
<p><i>This</i> is a <strong>first line</strong> of text.</p>
<p><em>ContextualToolbar</em> won't show for the first block element.</p>
<p><em>This</em> is a <strong>first line</strong> of text.</p>
<p><em>This</em> is a <strong>second line</strong> of text.</p>
<p><em>This</em> is the <strong>end</strong> of text.</p>
</div>
Expand Down
12 changes: 12 additions & 0 deletions tests/manual/contextualtoolbar/contextualtoolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
import ArticlePresets from '@ckeditor/ckeditor5-presets/src/article';
import ContextualToolbar from '../../../src/toolbar/contextual/contextualtoolbar';
import Range from '@ckeditor/ckeditor5-engine/src/model/range';

ClassicEditor.create( document.querySelector( '#editor' ), {
plugins: [ ArticlePresets, ContextualToolbar ],
Expand All @@ -16,6 +17,17 @@ ClassicEditor.create( document.querySelector( '#editor' ), {
} )
.then( editor => {
window.editor = editor;

const contextualToolbar = editor.plugins.get( 'ContextualToolbar' );

contextualToolbar.on( 'show', evt => {
const selectionRange = editor.document.selection.getFirstRange();
const blockRange = Range.createOn( editor.document.getRoot().getChild( 0 ) );

if ( selectionRange.containsRange( blockRange ) || selectionRange.isIntersecting( blockRange ) ) {
evt.stop();
}
}, { priority: 'high' } );
} )
.catch( err => {
console.error( err.stack );
Expand Down
1 change: 1 addition & 0 deletions tests/manual/contextualtoolbar/contextualtoolbar.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
1. Create a non–collapsed selection.
2. Create another non–collapsed selection but in another direction.
3. For each selection, a contextual toolbar should appear and the beginning/end of the selection, duplicating main editor toolbar.
4. Toolbar should not display when selection is placed in the first block element.
35 changes: 28 additions & 7 deletions tests/toolbar/contextual/contextualtoolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,29 @@ describe( 'ContextualToolbar', () => {
sinon.assert.calledOnce( balloonAddSpy );
} );

it( 'should not add #toolbarView to the #_balloon when all components inside #toolbarView are disabled', () => {
Array.from( contextualToolbar.toolbarView.items ).forEach( item => {
item.isEnabled = false;
} );
setData( editor.document, '<paragraph>b[a]r</paragraph>' );

contextualToolbar.show();
sinon.assert.notCalled( balloonAddSpy );
} );

it( 'should add #toolbarView to the #_balloon when at least one component inside does not have #isEnabled interface', () => {
Array.from( contextualToolbar.toolbarView.items ).forEach( item => {
item.isEnabled = false;
} );

delete contextualToolbar.toolbarView.items.get( 0 ).isEnabled;

setData( editor.document, '<paragraph>b[a]r</paragraph>' );

contextualToolbar.show();
sinon.assert.calledOnce( balloonAddSpy );
} );

describe( 'on #_selectionChangeDebounced event', () => {
let showSpy;

Expand Down Expand Up @@ -408,25 +431,23 @@ describe( 'ContextualToolbar', () => {
} );
} );

describe( 'beforeShow event', () => {
it( 'should fire `beforeShow` event just before panel shows', () => {
describe( 'show event', () => {
it( 'should fire `show` event just before panel shows', () => {
const spy = sinon.spy();

contextualToolbar.on( 'beforeShow', spy );
contextualToolbar.on( 'show', spy );
setData( editor.document, '<paragraph>b[a]r</paragraph>' );

contextualToolbar.show();
sinon.assert.calledOnce( spy );
} );

it( 'should not show the panel when `beforeShow` event is stopped', () => {
it( 'should not show the panel when `show` event is stopped', () => {
const balloonAddSpy = sandbox.spy( balloon, 'add' );

setData( editor.document, '<paragraph>b[a]r</paragraph>' );

contextualToolbar.on( 'beforeShow', ( evt, stop ) => {
stop();
} );
contextualToolbar.on( 'show', evt => evt.stop(), { priority: 'high' } );

contextualToolbar.show();
sinon.assert.notCalled( balloonAddSpy );
Expand Down

0 comments on commit d83d07d

Please sign in to comment.