From 7f22b7f89ec6423d33e42b80aaac8706b093906d Mon Sep 17 00:00:00 2001
From: Vahid Nesro <63849626+Vahid1919@users.noreply.github.com>
Date: Thu, 15 Aug 2024 14:06:31 +0200
Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=A4=94=20sd-carousel=20-=20numbere?=
=?UTF-8?q?d=20variant=20-=20show=20total=20clicks=20(#1247)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../components/carousel/carousel.stories.ts | 2 +-
.../src/components/carousel/carousel.test.ts | 87 +++++++++++++++++-
.../src/components/carousel/carousel.ts | 88 ++++++++++++++++---
3 files changed, 161 insertions(+), 16 deletions(-)
diff --git a/packages/components/src/components/carousel/carousel.stories.ts b/packages/components/src/components/carousel/carousel.stories.ts
index 4d979ee24..9e7f809fc 100644
--- a/packages/components/src/components/carousel/carousel.stories.ts
+++ b/packages/components/src/components/carousel/carousel.stories.ts
@@ -16,6 +16,7 @@ export default {
type: 'slot',
name: 'default',
value: `
+
Default slot 1
Default slot 2
Default slot 3
@@ -154,7 +155,6 @@ export const SlidesPerMove = {
y: { type: 'attribute', name: 'slides-per-move', values: [2] }
},
constants: [
- { type: 'attribute', name: 'loop', value: 'true' },
{ type: 'attribute', name: 'slides-per-page', value: 2 },
{
type: 'slot',
diff --git a/packages/components/src/components/carousel/carousel.test.ts b/packages/components/src/components/carousel/carousel.test.ts
index 1ad401a6f..84d373d80 100644
--- a/packages/components/src/components/carousel/carousel.test.ts
+++ b/packages/components/src/components/carousel/carousel.test.ts
@@ -1,7 +1,7 @@
import { aTimeout, expect, fixture, html, oneEvent } from '@open-wc/testing';
import { clickOnElement } from '../../internal/test.js';
+import SdCarousel from './carousel';
import sinon from 'sinon';
-import type SdCarousel from './carousel';
describe('', () => {
it('should render a carousel with default configuration', async () => {
@@ -576,4 +576,89 @@ describe('', () => {
});
});
});
+
+ describe('getPageCount', () => {
+ it('should return the correct page count when totalSlides is divisible by slidesPerPage', () => {
+ // Arrange
+ const totalSlides = 10;
+ const slidesPerPage = 2;
+ const slidesPerMove = 1;
+
+ // Act
+ const pageCount = SdCarousel.getPageCount(totalSlides, slidesPerPage, slidesPerMove);
+
+ // Assert
+ expect(pageCount).to.equal(9);
+ });
+
+ it('should return the correct page count when totalSlides is not divisible by slidesPerPage', () => {
+ // Arrange
+ const totalSlides = 11;
+ const slidesPerPage = 3;
+ const slidesPerMove = 2;
+
+ // Act
+ const pageCount = SdCarousel.getPageCount(totalSlides, slidesPerPage, slidesPerMove);
+
+ // Assert
+ expect(pageCount).to.equal(5);
+ });
+
+ it('should return 1 when totalSlides is less than or equal to slidesPerPage', () => {
+ // Arrange
+ const totalSlides = 3;
+ const slidesPerPage = 5;
+ const slidesPerMove = 1;
+
+ // Act
+ const pageCount = SdCarousel.getPageCount(totalSlides, slidesPerPage, slidesPerMove);
+
+ // Assert
+ expect(pageCount).to.equal(1);
+ });
+ });
+
+ describe('getCurrentPage', () => {
+ it('should return the correct current page when the active slide is the first slide', () => {
+ // Arrange
+ const totalSlides = 5;
+ const activeSlide = 0;
+ const slidesPerPage = 2;
+ const slidesPerMove = 1;
+
+ // Act
+ const currentPage = SdCarousel.getCurrentPage(totalSlides, activeSlide, slidesPerPage, slidesPerMove);
+
+ // Assert
+ expect(currentPage).to.equal(1);
+ });
+
+ it('should return the correct current page when the active slide is in the middle of the carousel', () => {
+ // Arrange
+ const totalSlides = 5;
+ const activeSlide = 2;
+ const slidesPerPage = 2;
+ const slidesPerMove = 1;
+
+ // Act
+ const currentPage = SdCarousel.getCurrentPage(totalSlides, activeSlide, slidesPerPage, slidesPerMove);
+
+ // Assert
+ expect(currentPage).to.equal(3);
+ });
+
+ it('should return the correct current page when the active slide is the last slide', () => {
+ // Arrange
+ const totalSlides = 5;
+ const activeSlide = 4;
+ const slidesPerPage = 2;
+ const slidesPerMove = 1;
+
+ // Act
+ const currentPage = SdCarousel.getCurrentPage(totalSlides, activeSlide, slidesPerPage, slidesPerMove);
+
+ // Assert
+ expect(currentPage).to.equal(5);
+ });
+ });
});
diff --git a/packages/components/src/components/carousel/carousel.ts b/packages/components/src/components/carousel/carousel.ts
index 6ffea8ef3..938fc0793 100644
--- a/packages/components/src/components/carousel/carousel.ts
+++ b/packages/components/src/components/carousel/carousel.ts
@@ -84,6 +84,12 @@ export default class SdCarousel extends SolidElement {
*/
@state() activeSlide = 0;
+ /**
+ * The current page of the carousel
+ * @internal
+ */
+ @state() currentPage = 1;
+
/**
* Boolean keeping track of the autoplay pause/play button
* @internal
@@ -141,12 +147,24 @@ export default class SdCarousel extends SolidElement {
this.mutationObserver.observe(this, { childList: true, subtree: false });
}
- private getPageCount() {
- return Math.ceil(this.getSlides().length / this.slidesPerPage);
+ static getPageCount(totalSlides: number, slidesPerPage: number, slidesPerMove: number) {
+ return Math.ceil((totalSlides - slidesPerPage) / slidesPerMove) + 1 > 0
+ ? Math.ceil((totalSlides - slidesPerPage) / slidesPerMove) + 1
+ : // Returns 1 if the total number of slides is less than the number of slides per page
+ 1;
}
- private getCurrentPage() {
- return Math.ceil(this.activeSlide / this.slidesPerPage);
+ static getCurrentPage(
+ totalSlides: number,
+ activeSlide: number,
+ slidesPerPage: number,
+ slidesPerMove: number
+ ): number {
+ return (
+ Math.ceil((totalSlides - slidesPerPage) / slidesPerMove) -
+ Math.ceil((totalSlides - slidesPerPage - activeSlide) / slidesPerMove) +
+ 1
+ );
}
private getSlides({ excludeClones = true }: { excludeClones?: boolean } = {}) {
@@ -289,6 +307,13 @@ export default class SdCarousel extends SolidElement {
@watch('activeSlide')
handelSlideChange() {
+ this.currentPage = SdCarousel.getCurrentPage(
+ this.getSlides().length,
+ this.activeSlide,
+ this.slidesPerPage,
+ this.slidesPerMove
+ );
+
const slides = this.getSlides();
slides.forEach((slide, i) => {
slide.classList.toggle('--is-active', i === this.activeSlide);
@@ -303,6 +328,10 @@ export default class SdCarousel extends SolidElement {
}
});
}
+
+ if (this.currentPage > SdCarousel.getPageCount(this.getSlides().length, this.slidesPerPage, this.slidesPerMove)) {
+ this.nextTillFirst();
+ }
}
@watch('slidesPerMove')
@@ -318,6 +347,8 @@ export default class SdCarousel extends SolidElement {
slide.style.setProperty('scroll-snap-align', 'none');
}
});
+
+ // this.handleScrollEnd();
}
@watch('autoplay')
@@ -342,7 +373,11 @@ export default class SdCarousel extends SolidElement {
canSnap = Math.abs(previousIndex - this.slidesPerMove) % this.slidesPerMove === 0;
}
- this.goToSlide(previousIndex, behavior);
+ if (this.currentPage - 1 === 0 && this.loop) {
+ this.goToSlide(this.activeSlide - this.slidesPerPage, behavior);
+ } else {
+ this.goToSlide(previousIndex, behavior);
+ }
}
/**
@@ -351,7 +386,27 @@ export default class SdCarousel extends SolidElement {
* @param behavior - The behavior used for scrolling.
*/
next(behavior: ScrollBehavior = 'smooth') {
- this.goToSlide(this.activeSlide + this.slidesPerMove, behavior);
+ if (
+ this.currentPage + 1 > SdCarousel.getPageCount(this.getSlides().length, this.slidesPerPage, this.slidesPerMove) &&
+ this.loop
+ ) {
+ this.nextTillFirst(behavior);
+ } else {
+ this.goToSlide(this.activeSlide + this.slidesPerMove, behavior);
+ }
+ }
+
+ nextTillFirst(behavior: ScrollBehavior = 'smooth') {
+ while (this.activeSlide !== 0) {
+ this.goToSlide(this.activeSlide + 1, behavior);
+ }
+
+ this.currentPage = SdCarousel.getCurrentPage(
+ this.getSlides().length,
+ this.activeSlide,
+ this.slidesPerPage,
+ this.slidesPerMove
+ );
}
/**
@@ -374,10 +429,11 @@ export default class SdCarousel extends SolidElement {
// Get the index of the next slide. For looping carousel it adds `slidesPerPage`
// to normalize the starting index in order to ignore the first nth clones.
- const nextSlideIndex = clamp(index + (loop ? slidesPerPage : 0), 0, slidesWithClones.length - 1);
+ const nextSlideIndex = clamp(index + (loop ? slidesPerPage : 0), 0, slidesWithClones.length + 1);
const nextSlide = slidesWithClones[nextSlideIndex];
const scrollContainerRect = scrollContainer.getBoundingClientRect();
+
const nextSlideRect = nextSlide.getBoundingClientRect();
scrollContainer.scrollTo({
@@ -389,10 +445,15 @@ export default class SdCarousel extends SolidElement {
render() {
const { scrollController, slidesPerPage } = this;
- const pagesCount = this.getPageCount();
- const currentPage = this.getCurrentPage();
- const prevEnabled = this.loop || currentPage > 0;
- const nextEnabled = this.loop || currentPage < pagesCount - 1;
+ const pagesCount = SdCarousel.getPageCount(this.getSlides().length, this.slidesPerPage, this.slidesPerMove);
+ const currentPage = SdCarousel.getCurrentPage(
+ this.getSlides().length,
+ this.activeSlide,
+ this.slidesPerPage,
+ this.slidesPerMove
+ );
+ const prevEnabled = this.loop || currentPage > 1;
+ const nextEnabled = this.loop || currentPage < pagesCount;
const isLtr = this.localize.dir() === 'ltr';
return html`
@@ -451,7 +512,7 @@ export default class SdCarousel extends SolidElement {
aria-controls="scroll-container"
>
${map(range(pagesCount), index => {
- const isActive = index === currentPage;
+ const isActive = index + 1 === currentPage;
return html`