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

t/23: The toolbar should support a vertical offset from the top of the web page #24

Merged
merged 5 commits into from
Jul 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion src/inlineeditorui.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import ComponentFactory from '@ckeditor/ckeditor5-ui/src/componentfactory';
import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
import enableToolbarKeyboardFocus from '@ckeditor/ckeditor5-ui/src/toolbar/enabletoolbarkeyboardfocus';
import normalizeToolbarConfig from '@ckeditor/ckeditor5-ui/src/toolbar/normalizetoolbarconfig';

/**
* The inline editor UI class.
Expand Down Expand Up @@ -44,9 +45,21 @@ export default class InlineEditorUI {
*/
this.focusTracker = new FocusTracker();

/**
* A normalized `config.toolbar` object.
*
* @type {Object}
* @private
*/
this._toolbarConfig = normalizeToolbarConfig( editor.config.get( 'toolbar' ) );

// Set–up the view#panel.
view.panel.bind( 'isVisible' ).to( this.focusTracker, 'isFocused' );

if ( this._toolbarConfig && this._toolbarConfig.viewportTopOffset ) {
view.viewportTopOffset = this._toolbarConfig.viewportTopOffset;
}

// https://github.com/ckeditor/ckeditor5-editor-inline/issues/4
view.listenTo( editor.editing.view, 'render', () => {
// Don't pin if the panel is not already visible. It prevents the panel
Expand Down Expand Up @@ -78,7 +91,10 @@ export default class InlineEditorUI {
const editor = this.editor;

this.view.init();
this.view.toolbar.fillFromConfig( editor.config.get( 'toolbar' ), this.componentFactory );

if ( this._toolbarConfig ) {
this.view.toolbar.fillFromConfig( this._toolbarConfig.items, this.componentFactory );
}

enableToolbarKeyboardFocus( {
origin: editor.editing.view,
Expand Down
189 changes: 103 additions & 86 deletions src/inlineeditoruiview.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,6 @@ import BalloonPanelView from '@ckeditor/ckeditor5-ui/src/panel/balloon/balloonpa
import ToolbarView from '@ckeditor/ckeditor5-ui/src/toolbar/toolbarview';
import Template from '@ckeditor/ckeditor5-ui/src/template';

// A set of positioning functions used by the
// {@link module:editor-inline/inlineeditoruiview~InlineEditableUIView#panel}.
//
// @private
// @type {module:utils/dom/position~Options#positions}
const panelPositions = [
( editableRect, panelRect ) => {
return {
top: getPanelPositionTop( editableRect, panelRect ),
left: editableRect.left,
name: 'toolbar_west'
};
},
( editableRect, panelRect ) => {
return {
top: getPanelPositionTop( editableRect, panelRect ),
left: editableRect.left + editableRect.width - panelRect.width,
name: 'toolbar_east'
};
}
];

/**
* Inline editor UI view. Uses inline editable and floating toolbar.
*
Expand All @@ -57,6 +35,21 @@ export default class InlineEditorUIView extends EditorUIView {
*/
this.toolbar = new ToolbarView( locale );

/**
* The offset from the top edge of the web browser's viewport which makes the
* UI become sticky. The default value is `0`, which means the UI becomes
* sticky when it's upper edge touches the top of the page viewport.
*
* This attribute is useful when the web page has UI elements positioned to the top
* either using `position: fixed` or `position: sticky`, which would cover the
* UI or vice–versa (depending on the `z-index` hierarchy).
*
* @readonly
* @observable
* @member {Number} #viewportTopOffset
*/
this.set( 'viewportTopOffset', 0 );

Template.extend( this.toolbar.template, {
attributes: {
class: [
Expand All @@ -78,6 +71,53 @@ export default class InlineEditorUIView extends EditorUIView {

this.panel.withArrow = false;

/**
* A set of positioning functions used by the {@link #panel} to float around
* {@link #editableElement}.
*
* The positioning functions are as follows:
*
* * West:
*
* [ Panel ]
* +------------------+
* | #editableElement |
* +------------------+
*
* +------------------+
* | #editableElement |
* |[ Panel ] |
* | |
* +------------------+
*
* +------------------+
* | #editableElement |
* +------------------+
* [ Panel ]
*
* * East:
*
* [ Panel ]
* +------------------+
* | #editableElement |
* +------------------+
*
* +------------------+
* | #editableElement |
* | [ Panel ]|
* | |
* +------------------+
*
* +------------------+
* | #editableElement |
* +------------------+
* [ Panel ]
*
* @readonly
* @type {module:utils/dom/position~Options#positions}
*/
this.panelPositions = this._getPanelPositions();

Template.extend( this.panel.template, {
attributes: {
class: 'ck-toolbar-container'
Expand Down Expand Up @@ -113,73 +153,50 @@ export default class InlineEditorUIView extends EditorUIView {
}

/**
* A set of positioning functions used by the {@link #panel} to float around
* {@link #editableElement}.
*
* The positioning functions are as follows:
*
* * West:
*
* [ Panel ]
* +------------------+
* | #editableElement |
* +------------------+
*
* +------------------+
* | #editableElement |
* |[ Panel ] |
* | |
* +------------------+
*
* +------------------+
* | #editableElement |
* +------------------+
* [ Panel ]
*
* * East:
* Determines panel top position of the {@link #panel} in {@link #panelPositions}.
*
* [ Panel ]
* +------------------+
* | #editableElement |
* +------------------+
*
* +------------------+
* | #editableElement |
* | [ Panel ]|
* | |
* +------------------+
*
* +------------------+
* | #editableElement |
* +------------------+
* [ Panel ]
*
* @readonly
* @type {module:utils/dom/position~Options#positions}
* @private
* @param {module:utils/dom/rect~Rect} editableRect Rect of the
* {@link module:editor-inline/inlineeditoruiview~InlineEditableUIView#editableElement}.
* @param {module:utils/dom/rect~Rect} panelRect Rect of the
* {@link module:editor-inline/inlineeditoruiview~InlineEditableUIView#panel}.
*/
get panelPositions() {
return panelPositions;
_getPanelPositionTop( editableRect, panelRect ) {
let top;

if ( editableRect.top > panelRect.height + this.viewportTopOffset ) {
top = editableRect.top - panelRect.height;
} else if ( editableRect.bottom > panelRect.height + this.viewportTopOffset + 50 ) {
top = this.viewportTopOffset;
} else {
top = editableRect.bottom;
}

return top;
}
}

// Determines panel top position for
// {@link module:editor-inline/inlineeditoruiview~InlineEditableUIView#panelPositions}
//
// @private
// @param {module:utils/dom/rect~Rect} editableRect Rect of the
// {@link module:editor-inline/inlineeditoruiview~InlineEditableUIView#editableElement}.
// @param {module:utils/dom/rect~Rect} panelRect Rect of the
// {@link module:editor-inline/inlineeditoruiview~InlineEditableUIView#panel}.
function getPanelPositionTop( editableRect, panelRect ) {
let top;

if ( editableRect.top > panelRect.height ) {
top = editableRect.top - panelRect.height;
} else if ( editableRect.bottom > panelRect.height + 50 ) {
top = 0;
} else {
top = editableRect.bottom;
/**
* Returns the positions for {@link #panelPositions}.
*
* @private
* @returns module:utils/dom/position~Options#positions
*/
_getPanelPositions() {
return [
( editableRect, panelRect ) => {
return {
top: this._getPanelPositionTop( editableRect, panelRect ),
left: editableRect.left,
name: 'toolbar_west'
};
},
( editableRect, panelRect ) => {
return {
top: this._getPanelPositionTop( editableRect, panelRect ),
left: editableRect.left + editableRect.width - panelRect.width,
name: 'toolbar_east'
};
}
];
}

return top;
}
88 changes: 77 additions & 11 deletions tests/inlineeditorui.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,24 @@ describe( 'InlineEditorUI', () => {
editorElement = document.createElement( 'div' );
document.body.appendChild( editorElement );

editor = new ClassicTestEditor( editorElement, {
return ClassicTestEditor.create( editorElement, {
toolbar: [ 'foo', 'bar' ]
} );
} )
.then( newEditor => {
editor = newEditor;

view = new InlineEditorUIView( editor.locale );
ui = new InlineEditorUI( editor, view );
editable = editor.editing.view.getRoot();
view = new InlineEditorUIView( editor.locale );
ui = new InlineEditorUI( editor, view );
editable = editor.editing.view.getRoot();

ui.componentFactory.add( 'foo', viewCreator( 'foo' ) );
ui.componentFactory.add( 'bar', viewCreator( 'bar' ) );
ui.componentFactory.add( 'foo', viewCreator( 'foo' ) );
ui.componentFactory.add( 'bar', viewCreator( 'bar' ) );
} );
} );

afterEach( () => {
editorElement.remove();
editor.destroy();
} );

describe( 'constructor()', () => {
Expand Down Expand Up @@ -65,6 +73,35 @@ describe( 'InlineEditorUI', () => {
expect( view.panel.isVisible ).to.be.true;
} );

it( 'doesn\'t set the view#viewportTopOffset, if not specified in the config', () => {
expect( view.viewportTopOffset ).to.equal( 0 );
} );

it( 'sets view#viewportTopOffset, if specified', () => {
editorElement = document.createElement( 'div' );
document.body.appendChild( editorElement );

return ClassicTestEditor.create( editorElement, {
toolbar: {
items: [ 'foo', 'bar' ],
viewportTopOffset: 100
}
} )
.then( editor => {
view = new InlineEditorUIView( editor.locale );
ui = new InlineEditorUI( editor, view );
editable = editor.editing.view.getRoot();

ui.componentFactory.add( 'foo', viewCreator( 'foo' ) );
ui.componentFactory.add( 'bar', viewCreator( 'bar' ) );

expect( view.viewportTopOffset ).to.equal( 100 );

editorElement.remove();
return editor.destroy();
} );
} );

// https://github.com/ckeditor/ckeditor5-editor-inline/issues/4
it( 'pin() is called on editor.editable.view#render', () => {
const spy = sinon.spy( view.panel, 'pin' );
Expand Down Expand Up @@ -133,11 +170,40 @@ describe( 'InlineEditorUI', () => {
sinon.assert.calledOnce( spy );
} );

it( 'fills view.toolbar#items with editor config', () => {
const spy = testUtils.sinon.spy( view.toolbar, 'fillFromConfig' );
describe( 'view.toolbar#items', () => {
it( 'are filled with the config.toolbar (specified as an Array)', () => {
const spy = testUtils.sinon.spy( view.toolbar, 'fillFromConfig' );

ui.init();
sinon.assert.calledWithExactly( spy, editor.config.get( 'toolbar' ), ui.componentFactory );
ui.init();
sinon.assert.calledWithExactly( spy, editor.config.get( 'toolbar' ), ui.componentFactory );
} );

it( 'are filled with the config.toolbar (specified as an Object)', () => {
editorElement = document.createElement( 'div' );
document.body.appendChild( editorElement );

return ClassicTestEditor.create( editorElement, {
toolbar: {
items: [ 'foo', 'bar' ],
viewportTopOffset: 100
}
} )
.then( editor => {
view = new InlineEditorUIView( editor.locale );
ui = new InlineEditorUI( editor, view );

ui.componentFactory.add( 'foo', viewCreator( 'foo' ) );
ui.componentFactory.add( 'bar', viewCreator( 'bar' ) );

const spy = testUtils.sinon.spy( view.toolbar, 'fillFromConfig' );

ui.init();
sinon.assert.calledWithExactly( spy,
editor.config.get( 'toolbar.items' ),
ui.componentFactory
);
} );
} );
} );

it( 'initializes keyboard navigation between view#toolbar and view#editable', () => {
Expand Down
Loading