At long last, improved keyboard accessibility for the media modal.

props lessbloat, grahamarmfield, sharonaustin, bramd.
see #23560.

Built from https://develop.svn.wordpress.org/trunk@28607


git-svn-id: http://core.svn.wordpress.org/trunk@28431 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Helen Hou-Sandí 2014-05-29 03:39:15 +00:00
parent c4243e53d0
commit 78d90bd443
7 changed files with 124 additions and 65 deletions

View File

@ -478,8 +478,7 @@
border-left: 0;
}
.media-router > a:active,
.media-router > a:focus {
.media-router > a:active {
outline: none;
}
@ -696,6 +695,16 @@
user-select: none;
}
.attachment:focus {
-webkit-box-shadow:
0 0 0 1px #5b9dd9,
0 0 2px 2px #5b9dd9;
box-shadow:
0 0 0 1px #5b9dd9,
0 0 2px 2px #5b9dd9;
outline: none;
}
.selected.attachment {
-webkit-box-shadow:
0 0 0 1px #fff,
@ -925,6 +934,7 @@
left: 300px;
bottom: 0;
overflow: auto;
outline: none;
}
.attachments-browser .instructions {

File diff suppressed because one or more lines are too long

View File

@ -478,8 +478,7 @@
border-right: 0;
}
.media-router > a:active,
.media-router > a:focus {
.media-router > a:active {
outline: none;
}
@ -696,6 +695,16 @@
user-select: none;
}
.attachment:focus {
-webkit-box-shadow:
0 0 0 1px #5b9dd9,
0 0 2px 2px #5b9dd9;
box-shadow:
0 0 0 1px #5b9dd9,
0 0 2px 2px #5b9dd9;
outline: none;
}
.selected.attachment {
-webkit-box-shadow:
0 0 0 1px #fff,
@ -925,6 +934,7 @@
right: 300px;
bottom: 0;
overflow: auto;
outline: none;
}
.attachments-browser .instructions {

File diff suppressed because one or more lines are too long

View File

@ -2313,6 +2313,12 @@
} else {
frame.close();
}
// Keep focus inside media modal
// after canceling a gallery
new media.view.FocusManager({
el: this.el
}).focus();
}
},
separateCancel: new media.View({
@ -2495,6 +2501,12 @@
}) );
this.controller.setState('gallery-edit');
// Keep focus inside media modal
// after jumping to gallery view
new media.view.FocusManager({
el: this.el
}).focus();
}
});
},
@ -2521,6 +2533,12 @@
}) );
this.controller.setState('playlist-edit');
// Keep focus inside media modal
// after jumping to playlist view
new media.view.FocusManager({
el: this.el
}).focus();
}
});
},
@ -2547,6 +2565,12 @@
}) );
this.controller.setState('video-playlist-edit');
// Keep focus inside media modal
// after jumping to video playlist view
new media.view.FocusManager({
el: this.el
}).focus();
}
});
},
@ -2956,6 +2980,10 @@
propagate: true,
freeze: true
});
this.focusManager = new media.view.FocusManager({
el: this.el
});
},
/**
* @returns {Object}
@ -3037,7 +3065,12 @@
return this;
}
this.$el.hide();
// Hide modal and remove restricted media modal tab focus once it's closed
this.$el.hide().undelegate( 'keydown' );
// Put focus back in useful location once modal is closed
$('#wpbody-content').focus();
this.propagate('close');
// If the `freeze` option is set, restore the container's scroll position.
@ -3098,6 +3131,9 @@
if ( 27 === event.which && this.$el.is(':visible') ) {
this.escape();
event.stopImmediatePropagation();
} else {
// Keep focus inside the media modal
this.focusManager;
}
}
});
@ -3117,15 +3153,8 @@
},
focus: function() {
if ( _.isUndefined( this.index ) ) {
return;
}
// Update our collection of `$tabbables`.
this.$tabbables = this.$(':tabbable');
// If tab is saved, focus it.
this.$tabbables.eq( this.index ).focus();
// Reset focus on first left menu item
$('.media-menu-item').first().focus();
},
/**
* @param {Object} event
@ -3136,37 +3165,23 @@
return;
}
// First try to update the index.
if ( _.isUndefined( this.index ) ) {
this.updateIndex( event );
}
// If we still don't have an index, bail.
if ( _.isUndefined( this.index ) ) {
return;
}
var index = this.index + ( event.shiftKey ? -1 : 1 );
if ( index >= 0 && index < this.$tabbables.length ) {
this.index = index;
} else {
delete this.index;
// Keep tab focus within media modal while it's open
if ( event.target === this.tabbableLast[0] && !event.shiftKey ) {
this.tabbableFirst.focus();
return false;
} else if ( event.target === this.tabbableFirst[0] && event.shiftKey ) {
this.tabbableLast.focus();
return false;
}
},
/**
* @param {Object} event
*/
updateIndex: function( event ) {
this.$tabbables = this.$(':tabbable');
var index = this.$tabbables.index( event.target );
if ( -1 === index ) {
delete this.index;
} else {
this.index = index;
}
// Resets tabbable elements
this.tabbables = $( ':tabbable', this.$el );
this.tabbableFirst = this.tabbables.filter( ':first' );
this.tabbableLast = this.tabbables.filter( ':last' );
}
});
@ -4397,6 +4412,11 @@
className: 'attachment',
template: media.template('attachment'),
attributes: {
tabIndex: 0,
role: 'checkbox'
},
events: {
'click .attachment-preview': 'toggleSelectionHandler',
'change [data-setting]': 'updateSetting',
@ -4405,7 +4425,8 @@
'change [data-setting] textarea': 'updateSetting',
'click .close': 'removeFromLibrary',
'click .check': 'removeFromSelection',
'click a': 'preventDefault'
'click a': 'preventDefault',
'keydown': 'toggleSelectionHandler'
},
buttons: {},
@ -4413,6 +4434,7 @@
initialize: function() {
var selection = this.options.selection;
this.$el.attr('aria-label', this.model.attributes.title).attr('aria-checked', false);
this.model.on( 'change:sizes change:uploading', this.render, this );
this.model.on( 'change:title', this._syncTitle, this );
this.model.on( 'change:caption', this._syncCaption, this );
@ -4517,6 +4539,10 @@
toggleSelectionHandler: function( event ) {
var method;
// Catch enter and space events
if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
return;
}
if ( event.shiftKey ) {
method = 'between';
} else if ( event.ctrlKey || event.metaKey ) {
@ -4573,6 +4599,10 @@
return;
}
// Fixes bug that loses focus when selecting a featured image
if ( !method ) {
method = 'add';
}
if ( method !== 'add' ) {
method = 'reset';
}
@ -4617,7 +4647,7 @@
return;
}
this.$el.addClass('selected');
this.$el.addClass('selected').attr('aria-checked', true);
},
/**
* @param {Backbone.Model} model
@ -4632,7 +4662,7 @@
if ( ! selection || ( collection && collection !== selection ) ) {
return;
}
this.$el.removeClass('selected');
this.$el.removeClass('selected').attr('aria-checked', false);
},
/**
* @param {Backbone.Model} model
@ -4865,6 +4895,10 @@
tagName: 'ul',
className: 'attachments',
attributes: {
tabIndex: -1
},
cssTemplate: media.template('attachments-css'),
events: {
@ -5579,6 +5613,12 @@
clear: function( event ) {
event.preventDefault();
this.collection.reset();
// Keep focus inside media modal
// after clear link is selected
new media.view.FocusManager({
el: this.el
}).focus();
}
});
@ -5903,12 +5943,6 @@
},
initialize: function() {
/**
* @member {wp.media.view.FocusManager}
*/
this.focusManager = new media.view.FocusManager({
el: this.el
});
/**
* call 'initialize' directly on the parent class
*/
@ -5922,7 +5956,6 @@
* call 'render' directly on the parent class
*/
media.view.Attachment.prototype.render.apply( this, arguments );
this.focusManager.focus();
return this;
},
/**
@ -5933,6 +5966,11 @@
if ( confirm( l10n.warnDelete ) ) {
this.model.destroy();
// Keep focus inside media modal
// after image is deleted
new media.view.FocusManager({
el: this.el
}).focus();
}
},
/**
@ -5988,13 +6026,6 @@
},
initialize: function() {
/**
* @member {wp.media.view.FocusManager}
*/
this.focusManager = new media.view.FocusManager({
el: this.el
});
this.model.on( 'change:compat', this.render, this );
},
/**
@ -6021,8 +6052,6 @@
this.views.detach();
this.$el.html( compat.item );
this.views.render();
this.focusManager.focus();
return this;
},
/**

File diff suppressed because one or more lines are too long

View File

@ -119,6 +119,16 @@ function wp_print_media_templates() {
if ( $is_IE && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 7') !== false )
$class .= ' ie7';
?>
<!--[if lte IE 8]>
<style>
.attachment:focus {
outline: #1e8cbe solid;
}
.selected.attachment {
outline: #1e8cbe solid;
}
</style>
<![endif]-->
<script type="text/html" id="tmpl-media-frame">
<div class="media-frame-menu"></div>
<div class="media-frame-title"></div>
@ -238,7 +248,7 @@ function wp_print_media_templates() {
<# } else if ( 'image' === data.type ) { #>
<div class="thumbnail">
<div class="centered">
<img src="{{ data.size.url }}" draggable="false" />
<img src="{{ data.size.url }}" draggable="false" alt="" />
</div>
</div>
<# } else { #>
@ -253,7 +263,7 @@ function wp_print_media_templates() {
<# } #>
<# if ( data.buttons.check ) { #>
<a class="check" href="#" title="<?php esc_attr_e('Deselect'); ?>"><div class="media-modal-icon"></div></a>
<a class="check" href="#" title="<?php esc_attr_e('Deselect'); ?>" tabindex="-1"><div class="media-modal-icon"></div></a>
<# } #>
</div>
<#