diff --git a/CHANGELOG.md b/CHANGELOG.md
index 09ea7f77d8..6ffccf3759 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
- Fixed required checkbox message displaying. [1963](https://github.com/bigcommerce/cornerstone/pull/1963)
## Draft
+- Added notifications on Carousel's content cahnge through 'Next/Prev' buttons. [#1986](https://github.com/bigcommerce/cornerstone/pull/1986)
- Provided sufficient & informative text along with the color swatches [#1976](https://github.com/bigcommerce/cornerstone/pull/1976)
- If multiple Pick List Options are applied, customers cannot select "none" on both. [#1975](https://github.com/bigcommerce/cornerstone/pull/1975)
- Moved phrase from compare.html to en.json for increasing localization. [#1972](https://github.com/bigcommerce/cornerstone/pull/1972)
diff --git a/assets/js/theme/common/carousel/index.js b/assets/js/theme/common/carousel/index.js
index 3b975e65db..a6b7ef8e1b 100644
--- a/assets/js/theme/common/carousel/index.js
+++ b/assets/js/theme/common/carousel/index.js
@@ -5,35 +5,54 @@ import {
tooltipSetup,
setTabindexes,
arrowAriaLabling,
+ updateTextWithLiveData,
heroCarouselSetup,
getRealSlidesQuantityAndCurrentSlide,
} from './utils';
+/**
+ * returns actualSlide and actualSlideCount
+ * based on provided carousel settings
+ * @param {Object} $slickSettings
+ * @returns {Object}
+ */
+const extractSlidesDetails = ({
+ slideCount, currentSlide, breakpointSettings, activeBreakpoint, $slider,
+}) => getRealSlidesQuantityAndCurrentSlide(
+ breakpointSettings,
+ activeBreakpoint,
+ currentSlide,
+ slideCount,
+ $slider.data('slick').slidesToScroll,
+);
+
+export const onCarouselClick = ({
+ data: $activeSlider,
+}) => {
+ const $parentContainer = $activeSlider.hasClass('productView-thumbnails') ? $('.productView-images') : $activeSlider;
+ const { actualSlideCount, actualSlide } = extractSlidesDetails($activeSlider[0].slick);
+ const $carouselContentElement = $('.js-carousel-content-change-message', $parentContainer);
+ const carouselContentInitText = $carouselContentElement.text();
+ const carouselContentAnnounceMessage = updateTextWithLiveData(carouselContentInitText, (actualSlide + 1), actualSlideCount);
+
+ $carouselContentElement.text(carouselContentAnnounceMessage);
+};
+
export const onCarouselChange = (event, carousel) => {
const {
options: { prevArrow, nextArrow },
- currentSlide,
- slideCount,
$prevArrow,
$nextArrow,
$dots,
$slider,
- breakpointSettings,
- activeBreakpoint,
} = carousel;
- const { actualSlideCount, actualSlide } = getRealSlidesQuantityAndCurrentSlide(
- breakpointSettings,
- activeBreakpoint,
- currentSlide,
- slideCount,
- $slider.data('slick').slidesToScroll,
- );
-
+ const { actualSlideCount, actualSlide } = extractSlidesDetails(carousel);
const $prevArrowNode = $prevArrow || $slider.find(prevArrow);
const $nextArrowNode = $nextArrow || $slider.find(nextArrow);
const dataArrowLabel = $slider.data('arrow-label');
+
if (dataArrowLabel) {
$prevArrowNode.attr('aria-label', dataArrowLabel);
$nextArrowNode.attr('aria-label', dataArrowLabel);
@@ -54,14 +73,13 @@ export default function () {
$carouselCollection.each((index, carousel) => {
// getting element using find to pass jest test
const $carousel = $(document).find(carousel);
-
- $carousel.on('init', onCarouselChange);
- $carousel.on('afterChange', onCarouselChange);
+ $carousel.on('init afterChange', onCarouselChange);
+ $carousel.on('click', '.slick-arrow, .js-carousel-dot', $carousel, onCarouselClick);
const isMultipleSlides = $carousel.children().length > 1;
const customPaging = isMultipleSlides
? () => (
- ''
+ ''
)
: () => {};
diff --git a/assets/js/theme/common/carousel/utils/arrowAriaLabling.js b/assets/js/theme/common/carousel/utils/arrowAriaLabling.js
index 1e449ce00d..8ae8982471 100644
--- a/assets/js/theme/common/carousel/utils/arrowAriaLabling.js
+++ b/assets/js/theme/common/carousel/utils/arrowAriaLabling.js
@@ -1,26 +1,19 @@
-const NUMBER = '[NUMBER]';
-const integerRegExp = /[0-9]+/;
-const lastIntegerRegExp = /(\d+)(?!.*\d)/;
+import updateTextWithLiveData from './updateTextWithLiveData';
export default ($prevArrow, $nextArrow, actualSlide, actualSlideCount) => {
if (actualSlideCount < 2) return;
if ($prevArrow.length === 0 || $nextArrow.length === 0) return;
const arrowAriaLabelBaseText = $prevArrow.attr('aria-label');
-
- const isInit = arrowAriaLabelBaseText.includes(NUMBER);
- const valueToReplace = isInit ? NUMBER : integerRegExp;
const currentSlideNumber = actualSlide + 1;
const prevSlideNumber = actualSlide === 0 ? actualSlideCount : currentSlideNumber - 1;
- const arrowLeftText = arrowAriaLabelBaseText
- .replace(valueToReplace, prevSlideNumber)
- .replace(lastIntegerRegExp, actualSlideCount);
+ const arrowLeftText = updateTextWithLiveData(arrowAriaLabelBaseText, prevSlideNumber, actualSlideCount);
+
$prevArrow.attr('aria-label', arrowLeftText);
const nextSlideNumber = actualSlide === actualSlideCount - 1 ? 1 : currentSlideNumber + 1;
- const arrowRightText = arrowAriaLabelBaseText
- .replace(valueToReplace, nextSlideNumber)
- .replace(lastIntegerRegExp, actualSlideCount);
+ const arrowRightText = updateTextWithLiveData(arrowAriaLabelBaseText, nextSlideNumber, actualSlideCount);
+
$nextArrow.attr('aria-label', arrowRightText);
};
diff --git a/assets/js/theme/common/carousel/utils/index.js b/assets/js/theme/common/carousel/utils/index.js
index 3227c05446..4c15ee62f4 100644
--- a/assets/js/theme/common/carousel/utils/index.js
+++ b/assets/js/theme/common/carousel/utils/index.js
@@ -1,5 +1,6 @@
export { default as heroCarouselSetup } from './heroCarouselSetup';
export { default as arrowAriaLabling } from './arrowAriaLabling';
+export { default as updateTextWithLiveData } from './updateTextWithLiveData';
export { default as dotsSetup } from './dotsSetup';
export { default as getRealSlidesQuantityAndCurrentSlide } from './getRealSlidesQuantityAndCurrentSlide';
export { default as setTabindexes } from './setTabindexes';
diff --git a/assets/js/theme/common/carousel/utils/tooltipSetup.js b/assets/js/theme/common/carousel/utils/tooltipSetup.js
index 8c14c8b35e..acb1bf4526 100644
--- a/assets/js/theme/common/carousel/utils/tooltipSetup.js
+++ b/assets/js/theme/common/carousel/utils/tooltipSetup.js
@@ -1,6 +1,5 @@
const carouselTooltipClass = 'carousel-tooltip';
const carouselTooltip = ``;
-
const setupTooltipAriaLabel = ($node) => {
const $existedTooltip = $node.find(`.${carouselTooltipClass}`);
@@ -17,7 +16,7 @@ const setupArrowTooltips = (...arrowNodes) => {
};
const setupDotTooltips = ($dots) => {
- $dots.children().each((idx, dot) => setupTooltipAriaLabel($(dot).find('button')));
+ $dots.children().each((idx, dot) => setupTooltipAriaLabel($('.js-carousel-dot', dot)));
};
export default ($prevArrow, $nextArrow, $dots) => {
diff --git a/assets/js/theme/common/carousel/utils/updateTextWithLiveData.js b/assets/js/theme/common/carousel/utils/updateTextWithLiveData.js
new file mode 100644
index 0000000000..facff645a6
--- /dev/null
+++ b/assets/js/theme/common/carousel/utils/updateTextWithLiveData.js
@@ -0,0 +1,11 @@
+const NUMBER = '[NUMBER]';
+const integerRegExp = /[0-9]+/;
+const lastIntegerRegExp = /(\d+)(?!.*\d)/;
+
+export default (textForChange, slideNumber, slideCount) => {
+ const valueToReplace = textForChange.includes(NUMBER) ? NUMBER : integerRegExp;
+
+ return textForChange
+ .replace(valueToReplace, slideNumber)
+ .replace(lastIntegerRegExp, slideCount);
+};
diff --git a/assets/js/theme/global/quick-view.js b/assets/js/theme/global/quick-view.js
index dc671bc087..c8271a6dea 100644
--- a/assets/js/theme/global/quick-view.js
+++ b/assets/js/theme/global/quick-view.js
@@ -4,7 +4,7 @@ import utils from '@bigcommerce/stencil-utils';
import ProductDetails from '../common/product-details';
import { defaultModal, modalTypes } from './modal';
import 'slick-carousel';
-import { onCarouselChange } from '../common/carousel';
+import { onCarouselChange, onCarouselClick } from '../common/carousel';
export default function (context) {
const modal = defaultModal();
@@ -24,8 +24,8 @@ export default function (context) {
const $carousel = modal.$content.find('[data-slick]');
if ($carousel.length) {
- $carousel.on('init', onCarouselChange);
- $carousel.on('afterChange', onCarouselChange);
+ $carousel.on('init afterChange', onCarouselChange);
+ $carousel.on('click', '.slick-arrow', $carousel, onCarouselClick);
$carousel.slick();
}
diff --git a/lang/en.json b/lang/en.json
index 0666215ed8..56e5535e76 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -902,6 +902,7 @@
},
"carousel": {
"arrowAriaLabel": "Go to slide [NUMBER] of",
+ "contentAnnounceMessage": "You are currently on slide [NUMBER] of",
"dotAriaLabel": "Slide number",
"activeDotAriaLabel": "active",
"playPauseButtonPlay": "Play",
diff --git a/templates/components/carousel-content-announcement.html b/templates/components/carousel-content-announcement.html
new file mode 100644
index 0000000000..e3e0eab7bb
--- /dev/null
+++ b/templates/components/carousel-content-announcement.html
@@ -0,0 +1,6 @@
+
+ {{lang 'carousel.contentAnnounceMessage'}} {{slides_length}}
+
diff --git a/templates/components/carousel.html b/templates/components/carousel.html
index 5781bc000e..b580b4fce5 100644
--- a/templates/components/carousel.html
+++ b/templates/components/carousel.html
@@ -60,9 +60,11 @@
{{/if}}
{{/each}}
- {{#and arrows (if carousel.slides.length '>' 1)}}
+ {{#if carousel.slides.length '>' 1}}
+ {{> components/carousel-content-announcement slides_length=carousel.slides.length}}
+ {{#if arrows}}
- {{/and}}
+ {{/if}}
{{#if play_pause_button}}