Accessibility: Media: Use the ARIA tabs pattern for the media modal menus.

The ARIA tabs pattern improves interaction for keyboard and assistive technologies users.
It gives the menu items proper roles, and `aria-selected` allows users of assistive technologies to know which tab is currently selected.

Props audrasjb, afercia, joedolson, karmatosed, melchoyce.
See #47149.

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


git-svn-id: http://core.svn.wordpress.org/trunk@46162 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Andrea Fercia 2019-09-30 19:37:58 +00:00
parent e534fdf5aa
commit 63e199abc7
8 changed files with 558 additions and 246 deletions

View File

@ -580,23 +580,29 @@
user-select: none;
}
.media-menu > a {
.media-menu .media-menu-item {
display: block;
box-sizing: border-box;
width: 100%;
position: relative;
padding: 8px 20px;
border: 0;
margin: 0;
line-height: 1.28571428;
padding: 8px 20px;
font-size: 14px;
line-height: 1.28571428;
background: transparent;
color: #0073aa;
text-align: right;
text-decoration: none;
cursor: pointer;
}
.media-menu > a:hover {
color: #0073aa;
.media-menu .media-menu-item:hover {
background: rgba(0, 0, 0, 0.04);
}
.media-menu > a:active {
.media-menu .media-menu-item:active {
color: #0073aa;
outline: none;
}
@ -606,6 +612,15 @@
font-weight: 600;
}
.media-menu .media-menu-item:focus {
box-shadow:
0 0 0 1px #5b9dd9,
0 0 2px 1px rgba(30, 140, 190, 0.8);
color: #124964;
/* Only visible in Windows High Contrast mode */
outline: 1px solid transparent;
}
.media-menu .separator {
height: 0;
margin: 12px 20px;
@ -621,42 +636,48 @@
padding: 0 6px;
margin: 0;
clear: both;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.media-router a {
transition: none;
}
.media-router > a {
.media-router .media-menu-item {
position: relative;
float: right;
padding: 8px 10px 9px;
border: 0;
margin: 0;
padding: 8px 10px 9px;
height: 18px;
line-height: 1.28571428;
font-size: 14px;
text-decoration: none;
background: transparent;
cursor: pointer;
transition: none;
}
.media-router > a:last-child {
.media-router .media-menu-item:last-child {
border-left: 0;
}
.media-router > a:active {
outline: none;
.media-router .media-menu-item:hover,
.media-router .media-menu-item:active {
color: #0073aa;
}
.media-router .active,
.media-router .active:hover {
color: #32373c;
color: #23282d;
}
.media-router .media-menu-item:focus {
box-shadow:
0 0 0 1px #5b9dd9,
0 0 2px 1px rgba(30, 140, 190, 0.8);
color: #124964;
/* Only visible in Windows High Contrast mode */
outline: 1px solid transparent;
}
.media-router .active,
.media-router > a.active:last-child {
.media-router .media-menu-item.active:last-child {
margin: -1px -1px 0;
background: #fff;
border: 1px solid #ddd;
@ -752,15 +773,6 @@
display: none;
}
.media-frame.hide-router .media-frame-title {
border-bottom: 1px solid #ddd;
box-shadow: 0 4px 4px -4px rgba(0, 0, 0, 0.1);
}
.media-frame-title .dashicons {
display: none;
}
.media-frame-title h1 {
padding: 0 16px;
font-size: 22px;
@ -768,6 +780,10 @@
margin: 0;
}
.wp-core-ui .button.media-frame-menu-toggle {
display: none;
}
.media-frame-title .suggested-dimensions {
font-size: 14px;
float: left;
@ -2251,27 +2267,60 @@
* Responsive layout
*/
@media only screen and (max-width: 900px) {
.media-modal .media-frame-title {
height: 40px;
}
.media-modal .media-frame-title h1 {
line-height: 2.22222222;
font-size: 18px;
}
.media-modal-close {
width: 42px;
height: 42px;
}
/* Drop-down menu */
.media-frame:not(.hide-menu) .media-frame-title,
.media-frame .media-frame-title {
position: static;
padding: 0 44px;
text-align: center;
}
.media-frame:not(.hide-menu) .media-frame-router,
.media-frame:not(.hide-menu) .media-frame-content,
.media-frame:not(.hide-menu) .media-frame-toolbar {
right: 0;
}
.media-frame:not(.hide-menu) .media-frame-router {
/* 40 title + (40 - 6) menu toggle button + 6 spacing */
top: 80px;
}
.media-frame:not(.hide-menu) .media-frame-content {
/* 80 + room for the tabs */
top: 114px;
}
.media-frame.hide-router .media-frame-content {
top: 80px;
}
.media-frame:not(.hide-menu) .media-frame-menu {
position: static;
width: 0;
}
.media-frame:not(.hide-menu) .media-menu {
display: none;
width: auto;
max-width: 80%;
overflow: auto;
z-index: 2000;
top: 50px;
right: -300px;
top: 75px;
right: 0;
left: auto;
bottom: auto;
padding: 5px 0;
@ -2279,7 +2328,7 @@
}
.media-frame:not(.hide-menu) .media-menu.visible {
right: 0;
display: block;
}
.media-frame:not(.hide-menu) .media-menu > a {
@ -2287,29 +2336,32 @@
font-size: 16px;
}
.media-frame:not(.hide-menu) .media-menu > a.active {
display: none;
}
.media-frame:not(.hide-menu) .media-menu .separator {
margin: 5px 10px;
}
.media-frame:not(.hide-menu) .media-frame-title {
right: 0;
.wp-core-ui .media-frame:not(.hide-menu) .button.media-frame-menu-toggle {
display: inline-flex;
align-items: center;
vertical-align: top;
min-height: 40px;
margin: -6px 6px 0;
padding: 0 12px 0 2px;
font-size: 0.875rem;
font-weight: 600;
text-decoration: none;
background: transparent;
}
.media-frame:not(.hide-menu) .media-frame-title .dashicons {
display: inline-block;
line-height: 2.5;
.wp-core-ui .button.media-frame-menu-toggle:hover,
.wp-core-ui .button.media-frame-menu-toggle:active {
background: transparent;
transform: none;
}
.media-frame:not(.hide-menu) .media-frame-title h1 {
color: #0073aa;
line-height: 3;
font-size: 18px;
float: right;
cursor: pointer;
.wp-core-ui .button.media-frame-menu-toggle:focus {
/* Only visible in Windows High Contrast mode */
outline: 1px solid transparent;
}
/* End drop-down menu */
@ -2561,31 +2613,6 @@
}
}
/* Landscape specific header override */
@media screen and (max-height: 400px) {
.media-menu,
.media-frame:not(.hide-menu) .media-menu {
top: 44px;
}
.media-frame-router {
top: 44px;
}
.media-frame-content {
top: 78px;
}
.attachments-browser .attachments {
top: 40px;
}
/* Prevent unnecessary scrolling on title input */
.embed-link-settings {
overflow: visible;
}
}
@media only screen and (min-width: 901px) and (max-height: 400px) {
.media-menu,
.media-frame:not(.hide-menu) .media-menu {
@ -2595,38 +2622,10 @@
}
@media only screen and (max-width: 480px) {
.media-modal-close {
top: -5px;
}
.media-modal .media-frame-title {
height: 40px;
}
.wp-core-ui.wp-customizer .media-button {
margin-top: 13px;
}
.media-modal .media-frame-title h1,
.media-frame:not(.hide-menu) .media-frame-title h1 {
font-size: 18px;
line-height: 2.22222222;
}
.media-frame:not(.hide-menu) .media-frame-title .dashicons {
line-height: 2;
}
.media-frame-router,
.media-frame:not(.hide-menu) .media-menu {
top: 40px;
padding-top: 0;
}
.media-frame-content {
top: 74px;
}
.media-frame.hide-router .media-frame-content {
top: 40px;
}

File diff suppressed because one or more lines are too long

View File

@ -580,23 +580,29 @@
user-select: none;
}
.media-menu > a {
.media-menu .media-menu-item {
display: block;
box-sizing: border-box;
width: 100%;
position: relative;
padding: 8px 20px;
border: 0;
margin: 0;
line-height: 1.28571428;
padding: 8px 20px;
font-size: 14px;
line-height: 1.28571428;
background: transparent;
color: #0073aa;
text-align: left;
text-decoration: none;
cursor: pointer;
}
.media-menu > a:hover {
color: #0073aa;
.media-menu .media-menu-item:hover {
background: rgba(0, 0, 0, 0.04);
}
.media-menu > a:active {
.media-menu .media-menu-item:active {
color: #0073aa;
outline: none;
}
@ -606,6 +612,15 @@
font-weight: 600;
}
.media-menu .media-menu-item:focus {
box-shadow:
0 0 0 1px #5b9dd9,
0 0 2px 1px rgba(30, 140, 190, 0.8);
color: #124964;
/* Only visible in Windows High Contrast mode */
outline: 1px solid transparent;
}
.media-menu .separator {
height: 0;
margin: 12px 20px;
@ -621,42 +636,48 @@
padding: 0 6px;
margin: 0;
clear: both;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.media-router a {
transition: none;
}
.media-router > a {
.media-router .media-menu-item {
position: relative;
float: left;
padding: 8px 10px 9px;
border: 0;
margin: 0;
padding: 8px 10px 9px;
height: 18px;
line-height: 1.28571428;
font-size: 14px;
text-decoration: none;
background: transparent;
cursor: pointer;
transition: none;
}
.media-router > a:last-child {
.media-router .media-menu-item:last-child {
border-right: 0;
}
.media-router > a:active {
outline: none;
.media-router .media-menu-item:hover,
.media-router .media-menu-item:active {
color: #0073aa;
}
.media-router .active,
.media-router .active:hover {
color: #32373c;
color: #23282d;
}
.media-router .media-menu-item:focus {
box-shadow:
0 0 0 1px #5b9dd9,
0 0 2px 1px rgba(30, 140, 190, 0.8);
color: #124964;
/* Only visible in Windows High Contrast mode */
outline: 1px solid transparent;
}
.media-router .active,
.media-router > a.active:last-child {
.media-router .media-menu-item.active:last-child {
margin: -1px -1px 0;
background: #fff;
border: 1px solid #ddd;
@ -752,15 +773,6 @@
display: none;
}
.media-frame.hide-router .media-frame-title {
border-bottom: 1px solid #ddd;
box-shadow: 0 4px 4px -4px rgba(0, 0, 0, 0.1);
}
.media-frame-title .dashicons {
display: none;
}
.media-frame-title h1 {
padding: 0 16px;
font-size: 22px;
@ -768,6 +780,10 @@
margin: 0;
}
.wp-core-ui .button.media-frame-menu-toggle {
display: none;
}
.media-frame-title .suggested-dimensions {
font-size: 14px;
float: right;
@ -2251,27 +2267,60 @@
* Responsive layout
*/
@media only screen and (max-width: 900px) {
.media-modal .media-frame-title {
height: 40px;
}
.media-modal .media-frame-title h1 {
line-height: 2.22222222;
font-size: 18px;
}
.media-modal-close {
width: 42px;
height: 42px;
}
/* Drop-down menu */
.media-frame:not(.hide-menu) .media-frame-title,
.media-frame .media-frame-title {
position: static;
padding: 0 44px;
text-align: center;
}
.media-frame:not(.hide-menu) .media-frame-router,
.media-frame:not(.hide-menu) .media-frame-content,
.media-frame:not(.hide-menu) .media-frame-toolbar {
left: 0;
}
.media-frame:not(.hide-menu) .media-frame-router {
/* 40 title + (40 - 6) menu toggle button + 6 spacing */
top: 80px;
}
.media-frame:not(.hide-menu) .media-frame-content {
/* 80 + room for the tabs */
top: 114px;
}
.media-frame.hide-router .media-frame-content {
top: 80px;
}
.media-frame:not(.hide-menu) .media-frame-menu {
position: static;
width: 0;
}
.media-frame:not(.hide-menu) .media-menu {
display: none;
width: auto;
max-width: 80%;
overflow: auto;
z-index: 2000;
top: 50px;
left: -300px;
top: 75px;
left: 0;
right: auto;
bottom: auto;
padding: 5px 0;
@ -2279,7 +2328,7 @@
}
.media-frame:not(.hide-menu) .media-menu.visible {
left: 0;
display: block;
}
.media-frame:not(.hide-menu) .media-menu > a {
@ -2287,29 +2336,32 @@
font-size: 16px;
}
.media-frame:not(.hide-menu) .media-menu > a.active {
display: none;
}
.media-frame:not(.hide-menu) .media-menu .separator {
margin: 5px 10px;
}
.media-frame:not(.hide-menu) .media-frame-title {
left: 0;
.wp-core-ui .media-frame:not(.hide-menu) .button.media-frame-menu-toggle {
display: inline-flex;
align-items: center;
vertical-align: top;
min-height: 40px;
margin: -6px 6px 0;
padding: 0 2px 0 12px;
font-size: 0.875rem;
font-weight: 600;
text-decoration: none;
background: transparent;
}
.media-frame:not(.hide-menu) .media-frame-title .dashicons {
display: inline-block;
line-height: 2.5;
.wp-core-ui .button.media-frame-menu-toggle:hover,
.wp-core-ui .button.media-frame-menu-toggle:active {
background: transparent;
transform: none;
}
.media-frame:not(.hide-menu) .media-frame-title h1 {
color: #0073aa;
line-height: 3;
font-size: 18px;
float: left;
cursor: pointer;
.wp-core-ui .button.media-frame-menu-toggle:focus {
/* Only visible in Windows High Contrast mode */
outline: 1px solid transparent;
}
/* End drop-down menu */
@ -2561,31 +2613,6 @@
}
}
/* Landscape specific header override */
@media screen and (max-height: 400px) {
.media-menu,
.media-frame:not(.hide-menu) .media-menu {
top: 44px;
}
.media-frame-router {
top: 44px;
}
.media-frame-content {
top: 78px;
}
.attachments-browser .attachments {
top: 40px;
}
/* Prevent unnecessary scrolling on title input */
.embed-link-settings {
overflow: visible;
}
}
@media only screen and (min-width: 901px) and (max-height: 400px) {
.media-menu,
.media-frame:not(.hide-menu) .media-menu {
@ -2595,38 +2622,10 @@
}
@media only screen and (max-width: 480px) {
.media-modal-close {
top: -5px;
}
.media-modal .media-frame-title {
height: 40px;
}
.wp-core-ui.wp-customizer .media-button {
margin-top: 13px;
}
.media-modal .media-frame-title h1,
.media-frame:not(.hide-menu) .media-frame-title h1 {
font-size: 18px;
line-height: 2.22222222;
}
.media-frame:not(.hide-menu) .media-frame-title .dashicons {
line-height: 2;
}
.media-frame-router,
.media-frame:not(.hide-menu) .media-menu {
top: 40px;
padding-top: 0;
}
.media-frame-content {
top: 74px;
}
.media-frame.hide-router .media-frame-content {
top: 40px;
}

File diff suppressed because one or more lines are too long

View File

@ -2924,7 +2924,7 @@ MediaFrame = Frame.extend(/** @lends wp.media.view.MediaFrame.prototype */{
regions: ['menu','title','content','toolbar','router'],
events: {
'click div.media-frame-title h1': 'toggleMenu'
'click .media-frame-menu-toggle': 'toggleMenu'
},
/**
@ -2976,13 +2976,78 @@ MediaFrame = Frame.extend(/** @lends wp.media.view.MediaFrame.prototype */{
this.on( 'title:create:default', this.createTitle, this );
this.title.mode('default');
this.on( 'title:render', function( view ) {
view.$el.append( '<span class="dashicons dashicons-arrow-down"></span>' );
});
// Bind default menu.
this.on( 'menu:create:default', this.createMenu, this );
// Set the menu ARIA tab panel attributes when the modal opens.
this.on( 'open', this.setMenuTabPanelAriaAttributes, this );
// Set the router ARIA tab panel attributes when the modal opens.
this.on( 'open', this.setRouterTabPanelAriaAttributes, this );
// Update the menu ARIA tab panel attributes when the content updates.
this.on( 'content:render', this.setMenuTabPanelAriaAttributes, this );
// Update the router ARIA tab panel attributes when the content updates.
this.on( 'content:render', this.setRouterTabPanelAriaAttributes, this );
},
/**
* Sets the attributes to be used on the menu ARIA tab panel.
*
* @since 5.3.0
*
* @returns {void}
*/
setMenuTabPanelAriaAttributes: function() {
var stateId = this.state().get( 'id' ),
tabPanelEl = this.$el.find( '.media-frame-tab-panel' ),
ariaLabelledby;
tabPanelEl.removeAttr( 'role aria-labelledby tabindex' );
if ( this.menuView && this.menuView.isVisible ) {
ariaLabelledby = 'menu-item-' + stateId;
// Set the tab panel attributes only if the tabs are visible.
tabPanelEl
.attr( {
role: 'tabpanel',
'aria-labelledby': ariaLabelledby,
tabIndex: '0'
} );
}
},
/**
* Sets the attributes to be used on the router ARIA tab panel.
*
* @since 5.3.0
*
* @returns {void}
*/
setRouterTabPanelAriaAttributes: function() {
var tabPanelEl = this.$el.find( '.media-frame-content' ),
ariaLabelledby;
tabPanelEl.removeAttr( 'role aria-labelledby tabindex' );
// On the Embed view the router menu is hidden.
if ( 'embed' === this.content._mode ) {
return;
}
// Set the tab panel attributes only if the tabs are visible.
if ( this.routerView && this.routerView.isVisible && this.content._mode ) {
ariaLabelledby = 'menu-item-' + this.content._mode;
tabPanelEl
.attr( {
role: 'tabpanel',
'aria-labelledby': ariaLabelledby,
tabIndex: '0'
} );
}
},
/**
* @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
*/
@ -3012,12 +3077,22 @@ MediaFrame = Frame.extend(/** @lends wp.media.view.MediaFrame.prototype */{
*/
createMenu: function( menu ) {
menu.view = new wp.media.view.Menu({
controller: this
controller: this,
attributes: {
role: 'tablist',
'aria-orientation': 'vertical'
}
});
this.menuView = menu.view;
},
toggleMenu: function() {
this.$el.find( '.media-menu' ).toggleClass( 'visible' );
toggleMenu: function( event ) {
var menu = this.$el.find( '.media-menu' );
menu.toggleClass( 'visible' );
$( event.target ).attr( 'aria-expanded', menu.hasClass( 'visible' ) );
},
/**
@ -3035,8 +3110,15 @@ MediaFrame = Frame.extend(/** @lends wp.media.view.MediaFrame.prototype */{
*/
createRouter: function( router ) {
router.view = new wp.media.view.Router({
controller: this
controller: this,
attributes: {
role: 'tablist',
'aria-orientation': 'horizontal'
}
});
this.routerView = router.view;
},
/**
* @param {Object} options
@ -3620,8 +3702,11 @@ Post = Select.extend(/** @lends wp.media.view.MediaFrame.Post.prototype */{
mainMenu: function( view ) {
view.set({
'library-separator': new wp.media.View({
className: 'separator',
priority: 100
className: 'separator',
priority: 100,
attributes: {
role: 'presentation'
}
})
});
},
@ -4520,6 +4605,8 @@ module.exports = Modal;
/* 56 */
/***/ (function(module, exports) {
var $ = jQuery;
/**
* wp.media.view.FocusManager
*
@ -4533,7 +4620,40 @@ module.exports = Modal;
var FocusManager = wp.media.View.extend(/** @lends wp.media.view.FocusManager.prototype */{
events: {
'keydown': 'constrainTabbing'
'keydown': 'focusManagementMode'
},
/**
* Initializes the Focus Manager.
*
* @param {object} options The Focus Manager options.
*
* @since 5.3.0
*
* @return {void}
*/
initialize: function( options ) {
this.mode = options.mode || 'constrainTabbing';
this.tabsAutomaticActivation = options.tabsAutomaticActivation || false;
},
/**
* Determines which focus management mode to use.
*
* @since 5.3.0
*
* @param {object} event jQuery event object.
*
* @returns {void}
*/
focusManagementMode: function( event ) {
if ( this.mode === 'constrainTabbing' ) {
this.constrainTabbing( event );
}
if ( this.mode === 'tabsNavigation' ) {
this.tabsNavigation( event );
}
},
/**
@ -4589,8 +4709,10 @@ var FocusManager = wp.media.View.extend(/** @lends wp.media.view.FocusManager.pr
},
/**
* Hides from assistive technologies all the body children except the
* provided element and other elements that should not be hidden.
* Hides from assistive technologies all the body children.
*
* Sets an `aria-hidden="true"` attribute on all the body children except
* the provided element and other elements that should not be hidden.
*
* The reason why we use `aria-hidden` is that `aria-modal="true"` is buggy
* in Safari 11.1 and support is spotty in other browsers. Also, `aria-modal="true"`
@ -4633,7 +4755,9 @@ var FocusManager = wp.media.View.extend(/** @lends wp.media.view.FocusManager.pr
},
/**
* Makes visible again to assistive technologies all body children
* Unhides from assistive technologies all the body children.
*
* Makes visible again to assistive technologies all the body children
* previously hidden and stored in this.ariaHiddenElements.
*
* @since 5.2.3
@ -4687,7 +4811,158 @@ var FocusManager = wp.media.View.extend(/** @lends wp.media.view.FocusManager.pr
*
* @since 5.2.3
*/
ariaHiddenElements: []
ariaHiddenElements: [],
/**
* Holds the jQuery collection of ARIA tabs.
*
* @since 5.3.0
*/
tabs: $(),
/**
* Sets up tabs in an ARIA tabbed interface.
*
* @since 5.3.0
*
* @param {object} event jQuery event object.
*
* @returns {void}
*/
setupAriaTabs: function() {
this.tabs = this.$( '[role="tab"]' );
// Set up initial attributes.
this.tabs.attr( {
'aria-selected': 'false',
tabIndex: '-1'
} );
// Set up attributes on the initially active tab.
this.tabs.filter( '.active' )
.removeAttr( 'tabindex' )
.attr( 'aria-selected', 'true' );
},
/**
* Enables arrows navigation within the ARIA tabbed interface.
*
* @since 5.3.0
*
* @param {object} event jQuery event object.
*
* @returns {void}
*/
tabsNavigation: function( event ) {
var orientation = 'horizontal',
keys = [ 32, 35, 36, 37, 38, 39, 40 ];
// Return if not Spacebar, End, Home, or Arrow keys.
if ( keys.indexOf( event.which ) === -1 ) {
return;
}
// Determine navigation direction.
if ( this.$el.attr( 'aria-orientation' ) === 'vertical' ) {
orientation = 'vertical';
}
// Make Up and Down arrow keys do nothing with horizontal tabs.
if ( orientation === 'horizontal' && [ 38, 40 ].indexOf( event.which ) !== -1 ) {
return;
}
// Make Left and Right arrow keys do nothing with vertical tabs.
if ( orientation === 'vertical' && [ 37, 39 ].indexOf( event.which ) !== -1 ) {
return;
}
this.switchTabs( event, this.tabs );
},
/**
* Switches tabs in the ARIA tabbed interface.
*
* @since 5.3.0
*
* @param {object} event jQuery event object.
*
* @returns {void}
*/
switchTabs: function( event ) {
var key = event.which,
index = this.tabs.index( $( event.target ) ),
newIndex;
switch ( key ) {
// Space bar: Activate current targeted tab.
case 32: {
this.activateTab( this.tabs[ index ] );
break;
}
// End key: Activate last tab.
case 35: {
event.preventDefault();
this.activateTab( this.tabs[ this.tabs.length - 1 ] );
break;
}
// Home key: Activate first tab.
case 36: {
event.preventDefault();
this.activateTab( this.tabs[ 0 ] );
break;
}
// Left and up keys: Activate previous tab.
case 37:
case 38: {
event.preventDefault();
newIndex = ( index - 1 ) < 0 ? this.tabs.length - 1 : index - 1;
this.activateTab( this.tabs[ newIndex ] );
break;
}
// Right and down keys: Activate next tab.
case 39:
case 40: {
event.preventDefault();
newIndex = ( index + 1 ) === this.tabs.length ? 0 : index + 1;
this.activateTab( this.tabs[ newIndex ] );
break;
}
}
},
/**
* Sets a single tab to be focusable and semantically selected.
*
* @since 5.3.0
*
* @param {object} tab The tab DOM element.
*
* @returns {void}
*/
activateTab: function( tab ) {
if ( ! tab ) {
return;
}
// The tab is a DOM element: no need for jQuery methods.
tab.focus();
// Handle automatic activation.
if ( this.tabsAutomaticActivation ) {
tab.removeAttribute( 'tabindex' );
tab.setAttribute( 'aria-selected', 'true' );
tab.click();
return;
}
// Handle manual activation.
$( tab ).on( 'click', function() {
tab.removeAttribute( 'tabindex' );
tab.setAttribute( 'aria-selected', 'true' );
} );
}
});
module.exports = FocusManager;
@ -5903,25 +6178,23 @@ var MenuItem;
* @augments Backbone.View
*/
MenuItem = wp.media.View.extend(/** @lends wp.media.view.MenuItem.prototype */{
tagName: 'a',
tagName: 'button',
className: 'media-menu-item',
attributes: {
href: '#'
type: 'button',
role: 'tab'
},
events: {
'click': '_click'
},
/**
* @param {Object} event
*/
_click: function( event ) {
var clickOverride = this.options.click;
if ( event ) {
event.preventDefault();
}
/**
* Allows to override the click event.
*/
_click: function() {
var clickOverride = this.options.click;
if ( clickOverride ) {
clickOverride.call( this );
@ -5935,14 +6208,17 @@ MenuItem = wp.media.View.extend(/** @lends wp.media.view.MenuItem.prototype */{
if ( state ) {
this.controller.setState( state );
// Toggle the menu visibility in the responsive view.
this.views.parent.$el.removeClass( 'visible' ); // TODO: or hide on any click, see below
}
},
/**
* @returns {wp.media.view.MenuItem} returns itself to allow chaining
*/
render: function() {
var options = this.options;
var options = this.options,
menuProperty = options.state || options.contentMode;
if ( options.text ) {
this.$el.text( options.text );
@ -5950,6 +6226,9 @@ MenuItem = wp.media.View.extend(/** @lends wp.media.view.MenuItem.prototype */{
this.$el.html( options.html );
}
// Set the menu item ID based on the frame state associated to the menu item.
this.$el.attr( 'id', 'menu-item-' + menuProperty );
return this;
}
});
@ -5983,15 +6262,30 @@ Menu = PriorityList.extend(/** @lends wp.media.view.Menu.prototype */{
ItemView: MenuItem,
region: 'menu',
/* TODO: alternatively hide on any click anywhere
events: {
'click': 'click'
attributes: {
role: 'tablist',
'aria-orientation': 'horizontal'
},
click: function() {
this.$el.removeClass( 'visible' );
initialize: function() {
this._views = {};
this.set( _.extend( {}, this._views, this.options.views ), { silent: true });
delete this.options.views;
if ( ! this.options.silent ) {
this.render();
}
// Initialize the Focus Manager.
this.focusManager = new wp.media.view.FocusManager( {
el: this.el,
mode: 'tabsNavigation'
} );
// The menu is always rendered and can be visible or hidden on some frames.
this.isVisible = true;
},
*/
/**
* @param {Object} options
@ -6010,6 +6304,9 @@ Menu = PriorityList.extend(/** @lends wp.media.view.Menu.prototype */{
*/
PriorityList.prototype.ready.apply( this, arguments );
this.visibility();
// Set up aria tabs initial attributes.
this.focusManager.setupAriaTabs();
},
set: function() {
@ -6035,6 +6332,9 @@ Menu = PriorityList.extend(/** @lends wp.media.view.Menu.prototype */{
hide = ! views || views.length < 2;
if ( this === view ) {
// Flag this menu as hidden or visible.
this.isVisible = ! hide;
// Set or remove a CSS class to hide the menu.
this.controller.$el.toggleClass( 'hide-' + region, hide );
}
},
@ -6050,6 +6350,9 @@ Menu = PriorityList.extend(/** @lends wp.media.view.Menu.prototype */{
this.deselect();
view.$el.addClass('active');
// Set up again the aria tabs initial attributes after the menu updates.
this.focusManager.setupAriaTabs();
},
deselect: function() {
@ -6136,6 +6439,11 @@ Router = Menu.extend(/** @lends wp.media.view.Router.prototype */{
ItemView: wp.media.view.RouterItem,
region: 'router',
attributes: {
role: 'tablist',
'aria-orientation': 'horizontal'
},
initialize: function() {
this.controller.on( 'content:render', this.update, this );
// Call 'initialize' directly on the parent class.

File diff suppressed because one or more lines are too long

View File

@ -178,9 +178,15 @@ function wp_print_media_templates() {
<?php // Template for the media frame: used both in the media grid and in the media modal. ?>
<script type="text/html" id="tmpl-media-frame">
<div class="media-frame-title" id="media-frame-title"></div>
<button type="button" class="button button-link media-frame-menu-toggle" aria-expanded="false">
<?php _e( 'Media Types' ); ?>
<span class="dashicons dashicons-arrow-down" aria-hidden="true"></span>
</button>
<div class="media-frame-menu"></div>
<div class="media-frame-router"></div>
<div class="media-frame-content"></div>
<div class="media-frame-tab-panel">
<div class="media-frame-router"></div>
<div class="media-frame-content"></div>
</div>
<div class="media-frame-toolbar"></div>
<div class="media-frame-uploader"></div>
</script>

View File

@ -13,7 +13,7 @@
*
* @global string $wp_version
*/
$wp_version = '5.3-beta1-46362';
$wp_version = '5.3-beta1-46363';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.