diff --git a/src/button/button.jsdoc b/src/button/button.jsdoc index 70f2cb0f..15c58f36 100644 --- a/src/button/button.jsdoc +++ b/src/button/button.jsdoc @@ -95,6 +95,14 @@ * @member {Boolean} #isVisible */ +/** + * Controls whether the button view is a toggle button (two–state) for assistive technologies. + * + * @observable + * @default false + * @member {Boolean} #isToggleable + */ + /** * (Optional) Controls whether the label of the button is hidden (e.g. an icon–only button). * diff --git a/src/button/buttonview.js b/src/button/buttonview.js index de86b701..9128888c 100644 --- a/src/button/buttonview.js +++ b/src/button/buttonview.js @@ -52,6 +52,7 @@ export default class ButtonView extends View { this.set( 'isEnabled', true ); this.set( 'isOn', false ); this.set( 'isVisible', true ); + this.set( 'isToggleable', false ); this.set( 'keystroke' ); this.set( 'label' ); this.set( 'tabindex', -1 ); @@ -132,7 +133,7 @@ export default class ButtonView extends View { tabindex: bind.to( 'tabindex' ), 'aria-labelledby': `ck-editor__aria-label_${ ariaLabelUid }`, 'aria-disabled': bind.if( 'isEnabled', true, value => !value ), - 'aria-pressed': bind.if( 'isOn', true ) + 'aria-pressed': bind.to( 'isOn', value => this.isToggleable ? String( value ) : false ) }, children: this.children, diff --git a/src/button/switchbuttonview.js b/src/button/switchbuttonview.js index f6b593f5..aee616ba 100644 --- a/src/button/switchbuttonview.js +++ b/src/button/switchbuttonview.js @@ -35,6 +35,8 @@ export default class SwitchButtonView extends ButtonView { constructor( locale ) { super( locale ); + this.isToggleable = true; + /** * The toggle switch of the button. * diff --git a/src/dropdown/button/splitbuttonview.js b/src/dropdown/button/splitbuttonview.js index 3b454ac5..ca4fd171 100644 --- a/src/dropdown/button/splitbuttonview.js +++ b/src/dropdown/button/splitbuttonview.js @@ -50,6 +50,7 @@ export default class SplitButtonView extends View { this.set( 'icon' ); this.set( 'isEnabled', true ); this.set( 'isOn', false ); + this.set( 'isToggleable', false ); this.set( 'isVisible', true ); this.set( 'keystroke' ); this.set( 'label' ); @@ -173,6 +174,7 @@ export default class SplitButtonView extends View { 'icon', 'isEnabled', 'isOn', + 'isToggleable', 'keystroke', 'label', 'tabindex', @@ -202,13 +204,15 @@ export default class SplitButtonView extends View { */ _createArrowView() { const arrowView = new ButtonView(); + const bind = arrowView.bindTemplate; arrowView.icon = dropdownArrowIcon; arrowView.extendTemplate( { attributes: { class: 'ck-splitbutton__arrow', - 'aria-haspopup': true + 'aria-haspopup': true, + 'aria-expanded': bind.to( 'isOn', value => String( value ) ) } } ); diff --git a/src/toolbar/block/blockbuttonview.js b/src/toolbar/block/blockbuttonview.js index 29a0e06a..69aeb970 100644 --- a/src/toolbar/block/blockbuttonview.js +++ b/src/toolbar/block/blockbuttonview.js @@ -34,6 +34,8 @@ export default class BlockButtonView extends ButtonView { // Hide button on init. this.isVisible = false; + this.isToggleable = true; + /** * Top offset. * diff --git a/tests/button/buttonview.js b/tests/button/buttonview.js index 7d3c403b..de3402f7 100644 --- a/tests/button/buttonview.js +++ b/tests/button/buttonview.js @@ -245,11 +245,20 @@ describe( 'ButtonView', () => { } ); it( '-pressed reacts to #isOn', () => { + view.isToggleable = true; view.isOn = true; expect( view.element.attributes[ 'aria-pressed' ].value ).to.equal( 'true' ); view.isOn = false; - expect( view.element.attributes[ 'aria-pressed' ] ).to.be.undefined; + expect( view.element.attributes[ 'aria-pressed' ].value ).to.equal( 'false' ); + } ); + + it( '-pressed is not present for non–toggleable button', () => { + view.isOn = true; + expect( view.element.hasAttribute( 'aria-pressed' ) ).to.be.false; + + view.isOn = false; + expect( view.element.hasAttribute( 'aria-pressed' ) ).to.be.false; } ); } ); diff --git a/tests/button/switchbuttonview.js b/tests/button/switchbuttonview.js index 5bf4763b..493d8f50 100644 --- a/tests/button/switchbuttonview.js +++ b/tests/button/switchbuttonview.js @@ -24,6 +24,10 @@ describe( 'SwitchButtonView', () => { it( 'sets CSS class', () => { expect( view.element.classList.contains( 'ck-switchbutton' ) ).to.be.true; } ); + + it( 'sets isToggleable flag to true', () => { + expect( view.isToggleable ).to.be.true; + } ); } ); describe( 'render', () => { diff --git a/tests/dropdown/button/splitbuttonview.js b/tests/dropdown/button/splitbuttonview.js index 07c2e297..29c51336 100644 --- a/tests/dropdown/button/splitbuttonview.js +++ b/tests/dropdown/button/splitbuttonview.js @@ -30,6 +30,14 @@ describe( 'SplitButtonView', () => { expect( view.actionView.element.classList.contains( 'ck-splitbutton__action' ) ).to.be.true; } ); + it( 'adds isToggleable to view#actionView', () => { + expect( view.actionView.isToggleable ).to.be.false; + + view.isToggleable = true; + + expect( view.actionView.isToggleable ).to.be.true; + } ); + it( 'creates view#arrowView', () => { expect( view.arrowView ).to.be.instanceOf( ButtonView ); expect( view.arrowView.element.classList.contains( 'ck-splitbutton__arrow' ) ).to.be.true; @@ -62,6 +70,14 @@ describe( 'SplitButtonView', () => { expect( view.element.classList.contains( 'ck-splitbutton_open' ) ).to.be.false; } ); + it( 'binds arrowView aria-expanded attribute to #isOn', () => { + view.arrowView.isOn = true; + expect( view.arrowView.element.getAttribute( 'aria-expanded' ) ).to.equal( 'true' ); + + view.arrowView.isOn = false; + expect( view.arrowView.element.getAttribute( 'aria-expanded' ) ).to.equal( 'false' ); + } ); + describe( 'activates keyboard navigation for the toolbar', () => { it( 'so "arrowright" on view#arrowView does nothing', () => { const keyEvtData = { diff --git a/tests/toolbar/block/blockbuttonview.js b/tests/toolbar/block/blockbuttonview.js index efdda72b..1c42e7d1 100644 --- a/tests/toolbar/block/blockbuttonview.js +++ b/tests/toolbar/block/blockbuttonview.js @@ -22,6 +22,10 @@ describe( 'BlockButtonView', () => { expect( view.element.classList.contains( 'ck-block-toolbar-button' ) ).to.be.true; } ); + it( 'should be initialized as toggleable button', () => { + expect( view.isToggleable ).to.be.true; + } ); + describe( 'DOM binding', () => { it( 'should react on `view#top` change', () => { view.top = 0;