diff --git a/CHANGELOG.md b/CHANGELOG.md index 41e73e6226..533ae87a3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Draft +- Added modal trap for for product details. [#1758](https://github.com/bigcommerce/cornerstone/pull/1758) +- Added tooltips for carousel Previous and Next buttons. [#1749](https://github.com/bigcommerce/cornerstone/pull/1749) + ## 4.9.0 (07-28-2020) - Added correct alt text on image change in product view. [#1747](https://github.com/bigcommerce/cornerstone/pull/1747) - Description tab is hidden in case of empty product descrioption. [#1746](https://github.com/bigcommerce/cornerstone/pull/1746) diff --git a/assets/js/theme/common/product-details.js b/assets/js/theme/common/product-details.js index 4caeda6337..da1f3cb078 100644 --- a/assets/js/theme/common/product-details.js +++ b/assets/js/theme/common/product-details.js @@ -2,7 +2,7 @@ import utils from '@bigcommerce/stencil-utils'; import 'foundation-sites/js/foundation/foundation'; import 'foundation-sites/js/foundation/foundation.reveal'; import ImageGallery from '../product/image-gallery'; -import modalFactory, { showAlertModal } from '../global/modal'; +import modalFactory, { showAlertModal, modalTypes } from '../global/modal'; import _ from 'lodash'; import Wishlist from '../wishlist'; import { normalizeFormData } from './utils/api'; @@ -392,7 +392,7 @@ export default class ProductDetails { if (this.previewModal) { this.previewModal.open(); - this.updateCartContent(this.previewModal, response.data.cart_item.id); + this.updateCartContent(this.previewModal, response.data.cart_item.id, () => this.previewModal.setupFocusableElements(modalTypes.PRODUCT_DETAILS)); } else { this.$overlay.show(); // if no modal, redirect to the cart page diff --git a/assets/js/theme/global/modal.js b/assets/js/theme/global/modal.js index 588bf86cdf..b39bdcc54f 100644 --- a/assets/js/theme/global/modal.js +++ b/assets/js/theme/global/modal.js @@ -7,7 +7,6 @@ const modalBodyClass = 'modal-body'; const modalContentClass = 'modal-content'; const allTabbableElementsSelector = ':tabbable'; -const inactiveTabbableElementsSelector = '[tabindex="-1"], [type="hidden"]'; const tabKeyCode = 9; const firstTabbableClass = 'first-tabbable'; const lastTabbableClass = 'last-tabbable'; @@ -20,14 +19,19 @@ const SizeClasses = { export const modalTypes = { QUICK_VIEW: 'forQuickView', + PRODUCT_DETAILS: 'forProductDetails', }; const focusableElements = { - [modalTypes.QUICK_VIEW]: () => $('#modal') - .find(allTabbableElementsSelector) - .not('#modal-review-form *') - .not('#previewModal *') - .not(inactiveTabbableElementsSelector), + [modalTypes.QUICK_VIEW]: () => ( + $('#modal') + .find(allTabbableElementsSelector) + .not('#modal-review-form *') + .not('#previewModal *') + ), + [modalTypes.PRODUCT_DETAILS]: () => ( + $('#previewModal').find(allTabbableElementsSelector) + ), }; export const ModalEvents = { @@ -218,11 +222,13 @@ export class Modal { setupFocusableElements(modalType) { this.$preModalFocusedEl = $(document.activeElement); + const $modalTabbableCollection = focusableElements[modalType](); - const $collection = focusableElements[modalType](); - $collection.get(0).focus(); - $('#modal').on('keydown', event => this.onTabbing(event, modalType)); + const elementToFocus = $modalTabbableCollection.get(0); + if (elementToFocus) elementToFocus.focus(); + + this.$modal.on('keydown', event => this.onTabbing(event, modalType)); } onTabbing(event, modalType) { @@ -230,13 +236,19 @@ export class Modal { if (!isTab) return; - const $tabbableCollection = focusableElements[modalType](); - const lastCollectionIdx = $tabbableCollection.length - 1; - const $firstTabbable = $tabbableCollection.get(0); - const $lastTabbable = $tabbableCollection.get(lastCollectionIdx); + const $modalTabbableCollection = focusableElements[modalType](); + const lastCollectionIdx = $modalTabbableCollection.length - 1; + const $firstTabbable = $modalTabbableCollection.get(0); + const $lastTabbable = $modalTabbableCollection.get(lastCollectionIdx); - $tabbableCollection.each((index, element) => { + $modalTabbableCollection.each((index, element) => { const $element = $(element); + + if ($modalTabbableCollection.length === 1) { + $element.addClass(`${firstTabbableClass} ${lastTabbableClass}`); + return false; + } + if ($element.is($firstTabbable)) { $element.addClass(firstTabbableClass).removeClass(lastTabbableClass); } else if ($element.is($lastTabbable)) { @@ -253,13 +265,13 @@ export class Modal { if (direction === 'forwards') { const isLastActive = $activeElement.hasClass(lastTabbableClass); if (isLastActive) { - $tabbableCollection.get(0).focus(); + $modalTabbableCollection.get(0).focus(); event.preventDefault(); } } else if (direction === 'backwards') { const isFirstActive = $activeElement.hasClass(firstTabbableClass); if (isFirstActive) { - $tabbableCollection.get(lastCollectionIdx).focus(); + $modalTabbableCollection.get(lastCollectionIdx).focus(); event.preventDefault(); } } @@ -272,7 +284,7 @@ export class Modal { onModalClosed() { this.size = this.defaultSize; if (this.$preModalFocusedEl) this.$preModalFocusedEl.focus(); - $('#modal').off(ModalEvents.keyDown); + this.$modal.off(ModalEvents.keyDown); this.unbindEvents(); } diff --git a/templates/components/common/modal.html b/templates/components/common/modal.html index f8e792b93f..be03417fb0 100644 --- a/templates/components/common/modal.html +++ b/templates/components/common/modal.html @@ -1,7 +1,7 @@ diff --git a/templates/components/products/product-view.html b/templates/components/products/product-view.html index ab8f188d95..a81ecd6e6d 100644 --- a/templates/components/products/product-view.html +++ b/templates/components/products/product-view.html @@ -263,9 +263,9 @@