Skip to content

Commit

Permalink
feat(storefront): BCTHEME-69 Add tooltips for carousel bullets (butto…
Browse files Browse the repository at this point in the history
…ns) (#1889)

Co-authored-by: BC-tymurbiedukhin <66319629+BC-tymurbiedukhin@users.noreply.github.com>
  • Loading branch information
yurytut1993 and BC-tymurbiedukhin authored Nov 17, 2020
1 parent 9a214a0 commit c43ea64
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 140 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

## Draft
- Add tooltips for carousel bullets (buttons). [#1889](https://github.com/bigcommerce/cornerstone/pull/1889)
- Cornerstone - Body text size above 14px is cut off on cart shipping dropdowns. [#1881](https://github.com/bigcommerce/cornerstone/pull/1881)
- Move Tax Field under Grand Total on Cart when Tax inclusive. [#1903](https://github.com/bigcommerce/cornerstone/pull/1903)
- Added styling config for the PayPal SPB on checkout page [#1866](https://github.com/bigcommerce/cornerstone/pull/1866)
Expand Down
136 changes: 0 additions & 136 deletions assets/js/theme/common/carousel.js

This file was deleted.

71 changes: 71 additions & 0 deletions assets/js/theme/common/carousel/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import 'slick-carousel';

import {
dotsSetup,
setTabindexes,
arrowAriaLabling,
heroCarouselSetup,
getRealSlidesQuantityAndCurrentSlide,
} from './utils';

const onCarouselChange = (event, carousel) => {
const {
options: { prevArrow, nextArrow },
currentSlide,
slideCount,
$dots,
$slider,
breakpointSettings,
activeBreakpoint,
} = carousel;

const { actualSlideCount, actualSlide } = getRealSlidesQuantityAndCurrentSlide(
breakpointSettings,
activeBreakpoint,
currentSlide,
slideCount,
$slider.data('slick').slidesToScroll,
);

const $prevArrow = $slider.find(prevArrow);
const $nextArrow = $slider.find(nextArrow);

dotsSetup($dots, actualSlide, actualSlideCount, $slider.data('dots-labels'));
setTabindexes($slider.find('.slick-slide'), $prevArrow, $nextArrow, actualSlide, actualSlideCount);
arrowAriaLabling($prevArrow, $nextArrow, actualSlide, actualSlideCount);
};

export default function () {
const $carouselCollection = $('[data-slick]');

if ($carouselCollection.length === 0) return;

$carouselCollection.each((index, carousel) => {
// getting element using find to pass jest test
const $carousel = $(document).find(carousel);

if ($carousel.hasClass('productView-thumbnails')) {
$carousel.slick();
return;
}

$carousel.on('init', onCarouselChange);
$carousel.on('afterChange', onCarouselChange);

const isMultipleSlides = $carousel.children().length > 1;
const customPaging = isMultipleSlides
? () => (
'<button type="button"></button>'
)
: () => {};

$carousel.slick({
accessibility: false,
arrows: isMultipleSlides,
customPaging,
dots: isMultipleSlides,
});
});

heroCarouselSetup($carouselCollection.filter('.heroCarousel'));
}
26 changes: 26 additions & 0 deletions assets/js/theme/common/carousel/utils/arrowAriaLabling.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const NUMBER = '[NUMBER]';
const integerRegExp = /[0-9]+/;
const lastIntegerRegExp = /(\d+)(?!.*\d)/;

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);
$prevArrow.attr('aria-label', arrowLeftText);

const nextSlideNumber = actualSlide === actualSlideCount - 1 ? 1 : currentSlideNumber + 1;
const arrowRightText = arrowAriaLabelBaseText
.replace(valueToReplace, nextSlideNumber)
.replace(lastIntegerRegExp, actualSlideCount);
$nextArrow.attr('aria-label', arrowRightText);
};
21 changes: 21 additions & 0 deletions assets/js/theme/common/carousel/utils/dotsSetup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export default ($dots, actualSlide, actualSlideCount, dotLabels) => {
if (!$dots) return;

if (actualSlideCount === 1) {
$dots.css('display', 'none');
return;
}

$dots.css('display', 'block');

const { dotAriaLabel, activeDotAriaLabel } = dotLabels;

$dots.children().each((index, dot) => {
const $dot = $(dot);
const dotSlideNumber = index + 1;
const dotAriaLabelComputed = index === actualSlide
? `${dotAriaLabel} ${dotSlideNumber}, ${activeDotAriaLabel}`
: `${dotAriaLabel} ${dotSlideNumber}`;
$dot.find('button').attr('aria-label', dotAriaLabelComputed);
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export default (
breakpointSettings,
activeBreakpoint,
currentSlide,
slideCount,
defaultSlidesToScrollQuantity = 1,
) => {
const slidesToScrollQuantity = activeBreakpoint
/* eslint-disable dot-notation */
? breakpointSettings[activeBreakpoint]['slidesToScroll']
: defaultSlidesToScrollQuantity;

return {
actualSlideCount: Math.ceil(slideCount / slidesToScrollQuantity),
actualSlide: Math.ceil(currentSlide / slidesToScrollQuantity),
};
};
59 changes: 59 additions & 0 deletions assets/js/theme/common/carousel/utils/heroCarouselSetup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const showCarouselIfSlidesAnalyzedSetup = ($carousel) => {
const analyzedSlides = [];
return ($slides) => ($slide) => {
analyzedSlides.push($slide);
if ($slides.length === analyzedSlides.length) {
$carousel.addClass('is-visible');
}
};
};

export default ($heroCarousel) => {
if ($heroCarousel.length === 0) return;

const $slidesNodes = $heroCarousel.find('.heroCarousel-slide');
const showCarouselIfSlidesAnalyzed = showCarouselIfSlidesAnalyzedSetup($heroCarousel)($slidesNodes);

$slidesNodes.each((index, element) => {
const $element = $(element);
const isContentBlock = !!$element.find('.heroCarousel-content').length;

if (isContentBlock) {
showCarouselIfSlidesAnalyzed($element);
return true;
}

const $image = $element.find('.heroCarousel-image-wrapper img');
$('<img/>')
.attr('src', $($image).attr('src'))
.load(function getImageSizes() {
const imageRealWidth = this.width;
const imageRealHeight = this.height;

const imageAspectRatio = imageRealHeight / imageRealWidth;

$element.addClass(() => {
switch (true) {
case imageAspectRatio > 0.8 && imageAspectRatio <= 1.2:
return 'is-square-image-type';
case imageAspectRatio > 1.2:
return 'is-vertical-image-type';
default:
return '';
}
});

showCarouselIfSlidesAnalyzed($element);
})
.error(() => {
showCarouselIfSlidesAnalyzed($element);
});
});

// Alternative image styling for IE, which doesn't support objectfit
if (document.documentElement.style.objectFit === undefined) {
$slidesNodes.each((index, element) => {
$(element).addClass('compat-object-fit');
});
}
};
5 changes: 5 additions & 0 deletions assets/js/theme/common/carousel/utils/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { default as heroCarouselSetup } from './heroCarouselSetup';
export { default as arrowAriaLabling } from './arrowAriaLabling';
export { default as dotsSetup } from './dotsSetup';
export { default as getRealSlidesQuantityAndCurrentSlide } from './getRealSlidesQuantityAndCurrentSlide';
export { default as setTabindexes } from './setTabindexes';
22 changes: 22 additions & 0 deletions assets/js/theme/common/carousel/utils/setTabindexes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const allFocusableElementsSelector = '[href], button, input, textarea, select, details, [contenteditable="true"], [tabindex]';

export default ($slides, $prevArrow, $nextArrow, actualSlide, actualSlideCount) => {
$slides.each((index, element) => {
const $element = $(element);
const tabIndex = $element.hasClass('slick-active') ? 0 : -1;
if (!$element.hasClass('js-product-slide')) {
$element.attr('tabindex', tabIndex);
}

$element.find(allFocusableElementsSelector).each((idx, child) => {
$(child).attr('tabindex', tabIndex);
});
});

if ($prevArrow.length === 0
|| $nextArrow.length === 0
|| $prevArrow.hasClass('js-hero-prev-arrow')) return;

$prevArrow.attr('aria-disabled', actualSlide === 0);
$nextArrow.attr('aria-disabled', actualSlide === actualSlideCount - 1);
};
4 changes: 3 additions & 1 deletion lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,9 @@
"page_builder_link": "Design this page in Page Builder"
},
"carousel": {
"arrowAriaLabel": "Go to slide [NUMBER] of"
"arrowAriaLabel": "Go to slide [NUMBER] of",
"dotAriaLabel": "Slide number",
"activeDotAriaLabel": "active"
},
"validation_messages": {
"valid_email": "You must enter a valid email.",
Expand Down
Loading

0 comments on commit c43ea64

Please sign in to comment.