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 #531 from ckeditor/i/5782
Browse files Browse the repository at this point in the history
Feature: Enabled keystroke preview in `ButtonView`. Implemented the `ButtonView#withKeystroke` property. Closes ckeditor/ckeditor5#5782.
  • Loading branch information
Reinmar authored Nov 25, 2019
2 parents 95404e3 + c239bc5 commit dac8ce0
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 2 deletions.
15 changes: 15 additions & 0 deletions src/button/button.jsdoc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
* (Optional) The keystroke associated with the button, i.e. <kbd>CTRL+B</kbd>,
* in the string format compatible with {@link module:utils/keyboard}.
*
* **Note**: Use {@link module:ui/button/button~Button#withKeystroke} if you want to display
* the keystroke information next to the {@link module:ui/button/button~Button#label label}.
*
* @observable
* @member {Boolean} #keystroke
*/
Expand Down Expand Up @@ -111,6 +114,18 @@
* @member {Boolean} #withText
*/

/**
* (Optional) Controls whether the keystroke of the button is displayed next to its
* {@link module:ui/button/button~Button#label label}.
*
* **Note**: This property requires a {@link module:ui/button/button~Button#keystroke keystroke}
* to be defined in the first place.
*
* @observable
* @default false
* @member {Boolean} #withKeystroke
*/

/**
* (Optional) An XML {@link module:ui/icon/iconview~IconView#content content} of the icon.
* When defined, an `iconView` should be added to the button.
Expand Down
48 changes: 47 additions & 1 deletion src/button/buttonview.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export default class ButtonView extends View {
this.set( 'tooltipPosition', 's' );
this.set( 'type', 'button' );
this.set( 'withText', false );
this.set( 'withKeystroke', false );

/**
* Collection of the child views inside of the button {@link #element}.
Expand Down Expand Up @@ -100,6 +101,16 @@ export default class ButtonView extends View {
}
} );

/**
* A view displaying the keystroke of the button next to the {@link #labelView label}.
* Added to {@link #children} when the {@link #withKeystroke `withKeystroke` attribute}
* is defined.
*
* @readonly
* @member {module:ui/view/view~View} #keystrokeView
*/
this.keystrokeView = this._createKeystrokeView();

/**
* Tooltip of the button bound to the template.
*
Expand Down Expand Up @@ -127,7 +138,8 @@ export default class ButtonView extends View {
bind.if( 'isEnabled', 'ck-disabled', value => !value ),
bind.if( 'isVisible', 'ck-hidden', value => !value ),
bind.to( 'isOn', value => value ? 'ck-on' : 'ck-off' ),
bind.if( 'withText', 'ck-button_with-text' )
bind.if( 'withText', 'ck-button_with-text' ),
bind.if( 'withKeystroke', 'ck-button_with-keystroke' ),
],
type: bind.to( 'type', value => value ? value : 'button' ),
tabindex: bind.to( 'tabindex' ),
Expand Down Expand Up @@ -171,6 +183,10 @@ export default class ButtonView extends View {

this.children.add( this.tooltipView );
this.children.add( this.labelView );

if ( this.withKeystroke ) {
this.children.add( this.keystrokeView );
}
}

/**
Expand Down Expand Up @@ -229,6 +245,36 @@ export default class ButtonView extends View {
return labelView;
}

/**
* Creates a view that displays a keystroke next to a {@link #labelView label }
* and binds it with button attributes.
*
* @private
* @returns {module:ui/view~View}
*/
_createKeystrokeView() {
const keystrokeView = new View();

keystrokeView.setTemplate( {
tag: 'span',

attributes: {
class: [
'ck',
'ck-button__keystroke'
]
},

children: [
{
text: this.bindTemplate.to( 'keystroke', text => getEnvKeystrokeText( text ) )
}
]
} );

return keystrokeView;
}

/**
* Gets the text for the {@link #tooltipView} from the combination of
* {@link #tooltip}, {@link #label} and {@link #keystroke} attributes.
Expand Down
70 changes: 69 additions & 1 deletion tests/button/buttonview.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import IconView from '../../src/icon/iconview';
import TooltipView from '../../src/tooltip/tooltipview';
import View from '../../src/view';
import ViewCollection from '../../src/viewcollection';
import env from '@ckeditor/ckeditor5-utils/src/env';

describe( 'ButtonView', () => {
let locale, view;
Expand All @@ -24,6 +25,10 @@ describe( 'ButtonView', () => {
view.render();
} );

afterEach( () => {
view.destroy();
} );

describe( 'constructor()', () => {
it( 'creates view#children collection', () => {
expect( view.children ).to.be.instanceOf( ViewCollection );
Expand All @@ -39,6 +44,10 @@ describe( 'ButtonView', () => {
expect( view.labelView.element.classList.contains( 'ck-button__label' ) ).to.be.true;
} );

it( 'creates #keystrokeView', () => {
expect( view.keystrokeView ).to.be.instanceOf( View );
} );

it( 'creates #iconView', () => {
expect( view.iconView ).to.be.instanceOf( IconView );
} );
Expand Down Expand Up @@ -86,6 +95,14 @@ describe( 'ButtonView', () => {
expect( view.element.classList.contains( 'ck-button_with-text' ) ).to.false;
} );

it( 'reacts on view#withKeystroke', () => {
view.withKeystroke = true;
expect( view.element.classList.contains( 'ck-button_with-keystroke' ) ).to.true;

view.withKeystroke = false;
expect( view.element.classList.contains( 'ck-button_with-keystroke' ) ).to.false;
} );

it( 'reacts on view#type', () => {
// Default value.
expect( view.element.getAttribute( 'type' ) ).to.equal( 'button' );
Expand Down Expand Up @@ -288,7 +305,7 @@ describe( 'ButtonView', () => {
} );
} );

describe( 'icon', () => {
describe( '#iconView', () => {
it( 'is omited in #children when view#icon is not defined', () => {
view = new ButtonView( locale );
view.render();
Expand Down Expand Up @@ -325,6 +342,57 @@ describe( 'ButtonView', () => {
} );
} );

describe( '#keystrokeView', () => {
it( 'is omited in #children when view#icon is not defined', () => {
view = new ButtonView( locale );
view.render();

expect( view.element.childNodes ).to.have.length( 2 );
expect( view.keystrokeView.element ).to.be.null;
} );

it( 'is added to the #children when view#withKeystroke is true', () => {
testUtils.sinon.stub( env, 'isMac' ).value( false );

view = new ButtonView( locale );
view.keystroke = 'Ctrl+A';
view.withKeystroke = true;
view.render();

expect( view.element.childNodes ).to.have.length( 3 );
expect( view.element.childNodes[ 2 ] ).to.equal( view.keystrokeView.element );

expect( view.keystrokeView.element.classList.contains( 'ck' ) ).to.be.true;
expect( view.keystrokeView.element.classList.contains( 'ck-button__keystroke' ) ).to.be.true;

expect( view.keystrokeView ).to.instanceOf( View );
expect( view.keystrokeView.element.textContent ).to.equal( 'Ctrl+A' );
} );

it( 'usese fancy kesytroke preview on Mac', () => {
testUtils.sinon.stub( env, 'isMac' ).value( true );

view = new ButtonView( locale );
view.keystroke = 'Ctrl+A';
view.withKeystroke = true;
view.render();

expect( view.keystrokeView.element.textContent ).to.equal( '⌘A' );
} );

it( 'is destroyed with the view', () => {
view = new ButtonView( locale );
view.keystroke = 'Ctrl+A';
view.withKeystroke = true;
view.render();

const spy = sinon.spy( view.keystrokeView, 'destroy' );

view.destroy();
sinon.assert.calledOnce( spy );
} );
} );

describe( 'focus()', () => {
it( 'focuses the button in DOM', () => {
const spy = sinon.spy( view.element, 'focus' );
Expand Down

0 comments on commit dac8ce0

Please sign in to comment.