瀏覽代碼

Merge pull request #500 from ckeditor/t/ckeditor5/1404

Fix: All editor toolbars should come with the `role` and `aria-label` attributes. Closes ckeditor/ckeditor5#1404.

BREAKING CHANGE: The `ToolbarView` class requires the [editor locale](https://ckeditor.com/docs/ckeditor5/latest/api/module_utils_locale-Locale.html) to be passed into the constructor.
Aleksander Nowodzinski 6 年之前
父節點
當前提交
a911da2a34

+ 3 - 1
packages/ckeditor5-ui/lang/contexts.json

@@ -4,5 +4,7 @@
 	"Edit block": "Label of the block toolbar icon (a block toolbar is displayed next to each paragraph, heading, list item, etc. and contains e.g. block formatting options)",
 	"Next": "Label for a button showing the next thing (tab, page, etc.).",
 	"Previous": "Label for a button showing the previous thing (tab, page, etc.).",
-	"%0 of %1": "Label for a 'page X of Y' status of a typical next/previous page navigation."
+	"%0 of %1": "Label for a 'page X of Y' status of a typical next/previous page navigation.",
+	"Editor toolbar": "Label used by assistive technologies describing a generic editor toolbar.",
+	"Dropdown toolbar": "Label used by assistive technologies describing a toolbar displayed inside a dropdown."
 }

+ 5 - 1
packages/ckeditor5-ui/src/dropdown/utils.js

@@ -129,7 +129,11 @@ export function createDropdown( locale, ButtonClass = DropdownButtonView ) {
  * @param {Iterable.<module:ui/button/buttonview~ButtonView>} buttons
  */
 export function addToolbarToDropdown( dropdownView, buttons ) {
-	const toolbarView = dropdownView.toolbarView = new ToolbarView();
+	const locale = dropdownView.locale;
+	const t = locale.t;
+	const toolbarView = dropdownView.toolbarView = new ToolbarView( locale );
+
+	toolbarView.set( 'ariaLabel', t( 'Dropdown toolbar' ) );
 
 	dropdownView.extendTemplate( {
 		attributes: {

+ 17 - 2
packages/ckeditor5-ui/src/toolbar/toolbarview.js

@@ -27,12 +27,25 @@ import { attachLinkToDocumentation } from '@ckeditor/ckeditor5-utils/src/ckedito
  */
 export default class ToolbarView extends View {
 	/**
-	 * @inheritDoc
+	 * Creates an instance of the {@link module:ui/toolbar/toolbarview~ToolbarView} class.
+	 *
+	 * Also see {@link #render}.
+	 *
+	 * @param {module:utils/locale~Locale} locale The localization services instance.
 	 */
 	constructor( locale ) {
 		super( locale );
 
 		const bind = this.bindTemplate;
+		const t = this.t;
+
+		/**
+		 * Label used by assistive technologies to describe this toolbar element.
+		 *
+		 * @default 'Editor toolbar'
+		 * @member {String} #ariaLabel
+		 */
+		this.set( 'ariaLabel', t( 'Editor toolbar' ) );
 
 		/**
 		 * Collection of the toolbar items (like buttons).
@@ -102,7 +115,9 @@ export default class ToolbarView extends View {
 					'ck-toolbar',
 					bind.if( 'isVertical', 'ck-toolbar_vertical' ),
 					bind.to( 'class' )
-				]
+				],
+				role: 'toolbar',
+				'aria-label': bind.to( 'ariaLabel' )
 			},
 
 			children: this.items,

+ 1 - 1
packages/ckeditor5-ui/tests/dropdown/manual/dropdown.js

@@ -93,7 +93,7 @@ function testLongLabel() {
 }
 
 function testToolbar() {
-	const locale = {};
+	const locale = { t: langString => langString };
 
 	const icons = { left: alignLeftIcon, right: alignRightIcon, center: alignCenterIcon };
 

+ 5 - 1
packages/ckeditor5-ui/tests/dropdown/utils.js

@@ -27,7 +27,7 @@ describe( 'utils', () => {
 	let locale, dropdownView;
 
 	beforeEach( () => {
-		locale = { t() {} };
+		locale = { t: langString => langString };
 	} );
 
 	describe( 'createDropdown()', () => {
@@ -272,6 +272,10 @@ describe( 'utils', () => {
 			expect( dropdownView.element.classList.contains( 'ck-toolbar-dropdown' ) ).to.be.true;
 		} );
 
+		it( 'sets aria-label', () => {
+			expect( dropdownView.toolbarView.element.getAttribute( 'aria-label' ) ).to.equal( 'Dropdown toolbar' );
+		} );
+
 		describe( 'view#toolbarView', () => {
 			it( 'is created', () => {
 				const panelChildren = dropdownView.panelView.children;

+ 2 - 2
packages/ckeditor5-ui/tests/toolbar/enabletoolbarkeyboardfocus.js

@@ -19,7 +19,7 @@ describe( 'enableToolbarKeyboardFocus()', () => {
 		origin = viewCreator();
 		originFocusTracker = new FocusTracker();
 		originKeystrokeHandler = new KeystrokeHandler();
-		toolbar = new ToolbarView();
+		toolbar = new ToolbarView( { t: langString => langString } );
 
 		toolbar.render();
 
@@ -93,7 +93,7 @@ describe( 'enableToolbarKeyboardFocus()', () => {
 		origin = viewCreator();
 		originFocusTracker = new FocusTracker();
 		originKeystrokeHandler = new KeystrokeHandler();
-		toolbar = new ToolbarView();
+		toolbar = new ToolbarView( { t: langString => langString } );
 
 		const toolbarFocusSpy = sinon.spy( toolbar, 'focus' );
 		const originFocusSpy = origin.focus = sinon.spy();

+ 45 - 2
packages/ckeditor5-ui/tests/toolbar/toolbarview.js

@@ -14,12 +14,30 @@ import FocusCycler from '../../src/focuscycler';
 import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
 import ViewCollection from '../../src/viewcollection';
 import View from '../../src/view';
+import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
+import { add as addTranslations, _clear as clearTranslations } from '@ckeditor/ckeditor5-utils/src/translation-service';
+import Locale from '@ckeditor/ckeditor5-utils/src/locale';
 
 describe( 'ToolbarView', () => {
 	let locale, view;
 
+	testUtils.createSinonSandbox();
+
+	before( () => {
+		addTranslations( 'pl', {
+			'Editor toolbar': 'Pasek narzędzi edytora'
+		} );
+		addTranslations( 'en', {
+			'Editor toolbar': 'Editor toolbar'
+		} );
+	} );
+
+	after( () => {
+		clearTranslations();
+	} );
+
 	beforeEach( () => {
-		locale = {};
+		locale = new Locale( 'en' );
 		view = new ToolbarView( locale );
 		view.render();
 	} );
@@ -61,6 +79,31 @@ describe( 'ToolbarView', () => {
 			expect( view.element.classList.contains( 'ck-toolbar' ) ).to.true;
 		} );
 
+		describe( 'attributes', () => {
+			it( 'should be defined', () => {
+				expect( view.element.getAttribute( 'role' ) ).to.equal( 'toolbar' );
+				expect( view.element.getAttribute( 'aria-label' ) ).to.equal( 'Editor toolbar' );
+			} );
+
+			it( 'should allow a custom aria-label', () => {
+				const view = new ToolbarView( locale );
+
+				view.ariaLabel = 'Custom label';
+
+				view.render();
+
+				expect( view.element.getAttribute( 'aria-label' ) ).to.equal( 'Custom label' );
+			} );
+
+			it( 'should allow the aria-label to be translated', () => {
+				const view = new ToolbarView( new Locale( 'pl' ) );
+
+				view.render();
+
+				expect( view.element.getAttribute( 'aria-label' ) ).to.equal( 'Pasek narzędzi edytora' );
+			} );
+		} );
+
 		describe( 'event listeners', () => {
 			it( 'prevent default on #mousedown', () => {
 				const evt = new Event( 'mousedown', { bubbles: true } );
@@ -116,7 +159,7 @@ describe( 'ToolbarView', () => {
 		} );
 
 		it( 'starts listening for #keystrokes coming from #element', () => {
-			const view = new ToolbarView();
+			const view = new ToolbarView( new Locale( 'en' ) );
 			const spy = sinon.spy( view.keystrokes, 'listenTo' );
 
 			view.render();