[Accessibility] Trap focus inside the product gallery modal (#50730)
* Add i18n_open_product_gallery prop to wc_single_product_params * Trap focus inside the product gallery modal * Ensure the product gallery controls are always visible * Add changelog file * Add space before parameter * Early return if there is no elements enough to trap focus * Revert changes on the gallery trigger link * Remove unnecessary white sopace * Fix trap focus when the arrow buttons are hidden * Remove usage of the Photoswipe destroy event
This commit is contained in:
parent
ff197207ef
commit
0f1634d6da
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: patch
|
||||||
|
Type: fix
|
||||||
|
|
||||||
|
Trap focus inside the product gallery modal.
|
|
@ -127,6 +127,8 @@ jQuery( function( $ ) {
|
||||||
this.onResetSlidePosition = this.onResetSlidePosition.bind( this );
|
this.onResetSlidePosition = this.onResetSlidePosition.bind( this );
|
||||||
this.getGalleryItems = this.getGalleryItems.bind( this );
|
this.getGalleryItems = this.getGalleryItems.bind( this );
|
||||||
this.openPhotoswipe = this.openPhotoswipe.bind( this );
|
this.openPhotoswipe = this.openPhotoswipe.bind( this );
|
||||||
|
this.trapFocusPhotoswipe = this.trapFocusPhotoswipe.bind( this );
|
||||||
|
this.handlePswpTrapFocus = this.handlePswpTrapFocus.bind( this );
|
||||||
|
|
||||||
if ( this.flexslider_enabled ) {
|
if ( this.flexslider_enabled ) {
|
||||||
this.initFlexslider( args.flexslider );
|
this.initFlexslider( args.flexslider );
|
||||||
|
@ -307,8 +309,10 @@ jQuery( function( $ ) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
var pswpElement = $( '.pswp' )[0],
|
var pswpElement = $( '.pswp' )[0],
|
||||||
items = this.getGalleryItems(),
|
items = this.getGalleryItems(),
|
||||||
eventTarget = $( e.target ),
|
eventTarget = $( e.target ),
|
||||||
|
currentTarget = e.currentTarget,
|
||||||
|
self = this,
|
||||||
clicked;
|
clicked;
|
||||||
|
|
||||||
if ( 0 < eventTarget.closest( '.woocommerce-product-gallery__trigger' ).length ) {
|
if ( 0 < eventTarget.closest( '.woocommerce-product-gallery__trigger' ).length ) {
|
||||||
|
@ -326,14 +330,73 @@ jQuery( function( $ ) {
|
||||||
}
|
}
|
||||||
captionEl.children[0].textContent = item.title;
|
captionEl.children[0].textContent = item.title;
|
||||||
return true;
|
return true;
|
||||||
}
|
},
|
||||||
|
timeToIdle: 0, // Ensure the gallery controls are always visible to avoid keyboard navigation issues.
|
||||||
}, wc_single_product_params.photoswipe_options );
|
}, wc_single_product_params.photoswipe_options );
|
||||||
|
|
||||||
// Initializes and opens PhotoSwipe.
|
// Initializes and opens PhotoSwipe.
|
||||||
var photoswipe = new PhotoSwipe( pswpElement, PhotoSwipeUI_Default, items, options );
|
var photoswipe = new PhotoSwipe( pswpElement, PhotoSwipeUI_Default, items, options );
|
||||||
|
|
||||||
|
photoswipe.listen( 'afterInit', function() {
|
||||||
|
self.trapFocusPhotoswipe( true );
|
||||||
|
});
|
||||||
|
|
||||||
|
photoswipe.listen( 'close', function() {
|
||||||
|
self.trapFocusPhotoswipe( false );
|
||||||
|
currentTarget.focus();
|
||||||
|
});
|
||||||
|
|
||||||
photoswipe.init();
|
photoswipe.init();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Control focus in photoswipe modal.
|
||||||
|
*
|
||||||
|
* @param {boolean} trapFocus - Whether to trap focus or not.
|
||||||
|
*/
|
||||||
|
ProductGallery.prototype.trapFocusPhotoswipe = function( trapFocus ) {
|
||||||
|
var pswp = document.querySelector( '.pswp' );
|
||||||
|
|
||||||
|
if ( ! pswp ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( trapFocus ) {
|
||||||
|
pswp.addEventListener( 'keydown', this.handlePswpTrapFocus );
|
||||||
|
} else {
|
||||||
|
pswp.removeEventListener( 'keydown', this.handlePswpTrapFocus );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle keydown event in photoswipe modal.
|
||||||
|
*/
|
||||||
|
ProductGallery.prototype.handlePswpTrapFocus = function( e ) {
|
||||||
|
var allFocusablesEls = e.currentTarget.querySelectorAll( 'button:not([disabled])' );
|
||||||
|
var filteredFocusablesEls = Array.from( allFocusablesEls ).filter( function( btn ) {
|
||||||
|
return btn.style.display !== 'none' && window.getComputedStyle( btn ).display !== 'none';
|
||||||
|
} );
|
||||||
|
|
||||||
|
if ( 1 >= filteredFocusablesEls.length ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstTabStop = filteredFocusablesEls[0];
|
||||||
|
var lastTabStop = filteredFocusablesEls[filteredFocusablesEls.length - 1];
|
||||||
|
|
||||||
|
if ( e.key === 'Tab' ) {
|
||||||
|
if ( e.shiftKey ) {
|
||||||
|
if ( document.activeElement === firstTabStop ) {
|
||||||
|
e.preventDefault();
|
||||||
|
lastTabStop.focus();
|
||||||
|
}
|
||||||
|
} else if ( document.activeElement === lastTabStop ) {
|
||||||
|
e.preventDefault();
|
||||||
|
firstTabStop.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to call wc_product_gallery on jquery selector.
|
* Function to call wc_product_gallery on jquery selector.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue