From 5831f5f1cd57ff6b211f80b12571a6baa61174b0 Mon Sep 17 00:00:00 2001 From: Tolu A Date: Mon, 8 Apr 2024 11:26:55 -0600 Subject: [PATCH 1/2] consolidate changes: handle edge case where slides are wider than the viewport --- .../fixtures/slidesToScroll-ltr.fixture.ts | 83 ++++++++++++++++ .../fixtures/slidesToScroll-rtl.fixture.ts | 83 ++++++++++++++++ .../src/__tests__/slidesToScroll-ltr.test.ts | 94 +++++++++++++++++- .../src/__tests__/slidesToScroll-rtl.test.ts | 98 ++++++++++++++++++- .../src/components/SlidesToScroll.ts | 4 +- 5 files changed, 358 insertions(+), 4 deletions(-) diff --git a/packages/embla-carousel/src/__tests__/fixtures/slidesToScroll-ltr.fixture.ts b/packages/embla-carousel/src/__tests__/fixtures/slidesToScroll-ltr.fixture.ts index 53b052bf..529f5154 100644 --- a/packages/embla-carousel/src/__tests__/fixtures/slidesToScroll-ltr.fixture.ts +++ b/packages/embla-carousel/src/__tests__/fixtures/slidesToScroll-ltr.fixture.ts @@ -163,3 +163,86 @@ export const FIXTURE_SLIDES_TO_SCROLL_LTR_2: TestElementDimensionsType = { value: 20 } } + +/* +Fixture 3 + +- Horizontal +- LTR +- No slide margins +- Slides are wider than viewport +*/ +export const FIXTURE_SLIDES_TO_SCROLL_LTR_3: TestElementDimensionsType = { + containerOffset: { + offsetWidth: 250, + offsetHeight: 190, + offsetTop: 0, + offsetLeft: 0 + }, + slideOffsets: [ + { + offsetWidth: 500, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: 0 + }, + { + offsetWidth: 500, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: 500 + }, + { + offsetWidth: 250, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: 1000 + }, + { + offsetWidth: 250, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: 1250 + }, + { + offsetWidth: 250, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: 1500 + }, + { + offsetWidth: 250, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: 1750 + }, + { + offsetWidth: 500, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: 2000 + }, + { + offsetWidth: 500, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: 2500 + }, + { + offsetWidth: 500, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: 3000 + }, + { + offsetWidth: 501, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: 3500 + } + ], + endMargin: { + property: 'marginRight', + value: 0 + } +} diff --git a/packages/embla-carousel/src/__tests__/fixtures/slidesToScroll-rtl.fixture.ts b/packages/embla-carousel/src/__tests__/fixtures/slidesToScroll-rtl.fixture.ts index 0bea0dd1..474c4b43 100644 --- a/packages/embla-carousel/src/__tests__/fixtures/slidesToScroll-rtl.fixture.ts +++ b/packages/embla-carousel/src/__tests__/fixtures/slidesToScroll-rtl.fixture.ts @@ -163,3 +163,86 @@ export const FIXTURE_SLIDES_TO_SCROLL_RTL_2: TestElementDimensionsType = { value: 20 } } + +/* +Fixture 3 + +- Horizontal +- RTL +- No slide margins +- Slides are wider than viewport +*/ +export const FIXTURE_SLIDES_TO_SCROLL_RTL_3: TestElementDimensionsType = { + containerOffset: { + offsetWidth: 250, + offsetHeight: 190, + offsetTop: 0, + offsetLeft: 0 + }, + slideOffsets: [ + { + offsetWidth: 500, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: 500 + }, + { + offsetWidth: 500, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: 0 + }, + { + offsetWidth: 250, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: -250 + }, + { + offsetWidth: 250, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: -500 + }, + { + offsetWidth: 250, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: -750 + }, + { + offsetWidth: 250, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: -1000 + }, + { + offsetWidth: 500, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: -1500 + }, + { + offsetWidth: 500, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: -2000 + }, + { + offsetWidth: 500, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: -2500 + }, + { + offsetWidth: 501, + offsetHeight: 0, + offsetTop: 0, + offsetLeft: -3001 + } + ], + endMargin: { + property: 'marginLeft', + value: 0 + } +} diff --git a/packages/embla-carousel/src/__tests__/slidesToScroll-ltr.test.ts b/packages/embla-carousel/src/__tests__/slidesToScroll-ltr.test.ts index d374b9b6..70b96713 100644 --- a/packages/embla-carousel/src/__tests__/slidesToScroll-ltr.test.ts +++ b/packages/embla-carousel/src/__tests__/slidesToScroll-ltr.test.ts @@ -3,7 +3,8 @@ import { defaultOptions } from '../components/Options' import { mockTestElements } from './mocks' import { FIXTURE_SLIDES_TO_SCROLL_LTR_1, - FIXTURE_SLIDES_TO_SCROLL_LTR_2 + FIXTURE_SLIDES_TO_SCROLL_LTR_2, + FIXTURE_SLIDES_TO_SCROLL_LTR_3 } from './fixtures/slidesToScroll-ltr.fixture' const FIRST_SNAP_INDEX = 0 @@ -126,6 +127,97 @@ describe('➡️ SlidesToScroll - Horizontal LTR', () => { }) }) + describe('"auto" is correct for slides WITHOUT MARGINS and:', () => { + describe('edge cases when slide is greater than viewport and:', () => { + const emblaApi = EmblaCarousel( + mockTestElements(FIXTURE_SLIDES_TO_SCROLL_LTR_3) + ) + + beforeEach(() => { + emblaApi.reInit({ ...defaultOptions, slidesToScroll: 'auto' }) + }) + + test('LOOP:FALSE', () => { + const engine = emblaApi.internalEngine() + const expectedScrollSnaps = [ + 0, -625, -1000, -1250, -1500, -1750, -2125, -2625, -3125, -3751 + ] + + expect(engine.scrollSnaps).toEqual(expectedScrollSnaps) + expect(engine.location.get()).toBe( + expectedScrollSnaps[FIRST_SNAP_INDEX] + ) + + expect(engine.slideRegistry).toEqual([ + [0], + [1], + [2], + [3], + [4], + [5], + [6], + [7], + [8], + [9] + ]) + }) + + test('LOOP:FALSE and CONTAINSCROLL:FALSE', () => { + emblaApi.reInit({ containScroll: false }) + + const engine = emblaApi.internalEngine() + const expectedScrollSnaps = [ + -125, -625, -1000, -1250, -1500, -1750, -2125, -2625, -3125, -3625.5 + ] + + expect(engine.scrollSnaps).toEqual(expectedScrollSnaps) + expect(engine.location.get()).toBe( + expectedScrollSnaps[FIRST_SNAP_INDEX] + ) + + expect(engine.slideRegistry).toEqual([ + [0], + [1], + [2], + [3], + [4], + [5], + [6], + [7], + [8], + [9] + ]) + }) + + test('LOOP:TRUE', () => { + emblaApi.reInit({ loop: true }) + + const engine = emblaApi.internalEngine() + const expectedScrollSnaps = [ + -125, -625, -1000, -1250, -1500, -1750, -2125, -2625, -3125, -3625.5 + ] + + expect(engine.scrollSnaps).toEqual(expectedScrollSnaps) + expect(engine.location.get()).toBe( + expectedScrollSnaps[FIRST_SNAP_INDEX] + ) + + expect(engine.slideRegistry).toEqual([ + [0], + [1], + [2], + [3], + [4], + [5], + [6], + [7], + [8], + [9] + ]) + }) + }) + }) + describe('"Custom number 2" is correct for slides WITHOUT MARGINS and:', () => { const emblaApi = EmblaCarousel( mockTestElements(FIXTURE_SLIDES_TO_SCROLL_LTR_1) diff --git a/packages/embla-carousel/src/__tests__/slidesToScroll-rtl.test.ts b/packages/embla-carousel/src/__tests__/slidesToScroll-rtl.test.ts index 7660e8f6..5d76f944 100644 --- a/packages/embla-carousel/src/__tests__/slidesToScroll-rtl.test.ts +++ b/packages/embla-carousel/src/__tests__/slidesToScroll-rtl.test.ts @@ -3,7 +3,8 @@ import { defaultOptions } from '../components/Options' import { mockTestElements } from './mocks' import { FIXTURE_SLIDES_TO_SCROLL_RTL_1, - FIXTURE_SLIDES_TO_SCROLL_RTL_2 + FIXTURE_SLIDES_TO_SCROLL_RTL_2, + FIXTURE_SLIDES_TO_SCROLL_RTL_3 } from './fixtures/slidesToScroll-rtl.fixture' const FIRST_SNAP_INDEX = 0 @@ -72,6 +73,101 @@ describe('➡️ SlidesToScroll - Horizontal RTL', () => { }) }) + describe('"auto" is correct for slides WITHOUT MARGINS and:', () => { + describe('edge cases when slide is greater than viewport and:', () => { + const emblaApi = EmblaCarousel( + mockTestElements(FIXTURE_SLIDES_TO_SCROLL_RTL_3) + ) + + beforeEach(() => { + emblaApi.reInit({ + ...defaultOptions, + slidesToScroll: 'auto', + direction: 'rtl' + }) + }) + + test('LOOP:FALSE', () => { + const engine = emblaApi.internalEngine() + const expectedScrollSnaps = [ + 0, -375, -250, -500, -750, -1000, -1375, -1875, -2375, -3001 + ] + + expect(engine.scrollSnaps).toEqual(expectedScrollSnaps) + expect(engine.location.get()).toBe( + expectedScrollSnaps[FIRST_SNAP_INDEX] + ) + + expect(engine.slideRegistry).toEqual([ + [0], + [1], + [2], + [3], + [4], + [5], + [6], + [7], + [8], + [9] + ]) + }) + + test('LOOP:FALSE and CONTAINSCROLL:FALSE', () => { + emblaApi.reInit({ containScroll: false }) + + const engine = emblaApi.internalEngine() + const expectedScrollSnaps = [ + -875, -375, -250, -500, -750, -1000, -1375, -1875, -2375, -2875.5 + ] + + expect(engine.scrollSnaps).toEqual(expectedScrollSnaps) + expect(engine.location.get()).toBe( + expectedScrollSnaps[FIRST_SNAP_INDEX] + ) + + expect(engine.slideRegistry).toEqual([ + [0], + [1], + [2], + [3], + [4], + [5], + [6], + [7], + [8], + [9] + ]) + }) + + test('LOOP:TRUE', () => { + emblaApi.reInit({ loop: true }) + + const engine = emblaApi.internalEngine() + const expectedScrollSnaps = [ + -875, -375, -250, -500, -750, -1000, -1375, -1875, -2375, -2875.5 + ] + + expect(engine.scrollSnaps).toEqual(expectedScrollSnaps) + expect(engine.location.get()).toBe( + expectedScrollSnaps[FIRST_SNAP_INDEX] + ) + + expect(engine.slideRegistry).toEqual([ + [0], + [1], + [2], + [3], + [4], + [5], + [6], + [7], + [8], + [9] + ]) + }) + }) + }) + describe('"auto" is correct for slides WITH MARGINS and:', () => { const emblaApi = EmblaCarousel( mockTestElements(FIXTURE_SLIDES_TO_SCROLL_RTL_2) diff --git a/packages/embla-carousel/src/components/SlidesToScroll.ts b/packages/embla-carousel/src/components/SlidesToScroll.ts index a71e1687..aa81eee8 100644 --- a/packages/embla-carousel/src/components/SlidesToScroll.ts +++ b/packages/embla-carousel/src/components/SlidesToScroll.ts @@ -38,7 +38,7 @@ export function SlidesToScroll( if (!array.length) return [] return arrayKeys(array) - .reduce((groups: number[], rectB) => { + .reduce((groups: number[], rectB, index) => { const rectA = arrayLast(groups) || 0 const isFirst = rectA === 0 const isLast = rectB === arrayLastIndex(array) @@ -49,7 +49,7 @@ export function SlidesToScroll( const gapB = !loop && isLast ? direction(endGap) : 0 const chunkSize = mathAbs(edgeB - gapB - (edgeA + gapA)) - if (chunkSize > viewSize + pixelTolerance) groups.push(rectB) + if (index && chunkSize > viewSize + pixelTolerance) groups.push(rectB) if (isLast) groups.push(array.length) return groups }, []) From 6a77cb255438a1296ef997a846123b3c183c9a6b Mon Sep 17 00:00:00 2001 From: Tolu A Date: Mon, 8 Apr 2024 18:53:53 -0600 Subject: [PATCH 2/2] handle edge case where slides are taller than the viewport --- .../slidesToScroll-vertical.fixture.ts | 82 ++++++++++++++++ .../src/__tests__/slidesToScroll-ltr.test.ts | 2 +- .../src/__tests__/slidesToScroll-rtl.test.ts | 2 +- .../__tests__/slidesToScroll-vertical.test.ts | 98 ++++++++++++++++++- 4 files changed, 181 insertions(+), 3 deletions(-) diff --git a/packages/embla-carousel/src/__tests__/fixtures/slidesToScroll-vertical.fixture.ts b/packages/embla-carousel/src/__tests__/fixtures/slidesToScroll-vertical.fixture.ts index 9536f1ee..a6c19b3b 100644 --- a/packages/embla-carousel/src/__tests__/fixtures/slidesToScroll-vertical.fixture.ts +++ b/packages/embla-carousel/src/__tests__/fixtures/slidesToScroll-vertical.fixture.ts @@ -161,3 +161,85 @@ export const FIXTURE_SLIDES_TO_SCROLL_Y_2: TestElementDimensionsType = { value: 20 } } + +/* +Fixture 3 + +- Vertical +- No slide margins +- Slides are taller than viewport +*/ +export const FIXTURE_SLIDES_TO_SCROLL_Y_3: TestElementDimensionsType = { + containerOffset: { + offsetWidth: 1000, + offsetHeight: 250, + offsetTop: 0, + offsetLeft: 0 + }, + slideOffsets: [ + { + offsetWidth: 1000, + offsetHeight: 500, + offsetTop: 0, + offsetLeft: 0 + }, + { + offsetWidth: 1000, + offsetHeight: 500, + offsetTop: 500, + offsetLeft: 0 + }, + { + offsetWidth: 1000, + offsetHeight: 250, + offsetTop: 1000, + offsetLeft: 0 + }, + { + offsetWidth: 1000, + offsetHeight: 250, + offsetTop: 1250, + offsetLeft: 0 + }, + { + offsetWidth: 1000, + offsetHeight: 250, + offsetTop: 1500, + offsetLeft: 0 + }, + { + offsetWidth: 1000, + offsetHeight: 250, + offsetTop: 1750, + offsetLeft: 0 + }, + { + offsetWidth: 1000, + offsetHeight: 500, + offsetTop: 2000, + offsetLeft: 0 + }, + { + offsetWidth: 1000, + offsetHeight: 500, + offsetTop: 2500, + offsetLeft: 0 + }, + { + offsetWidth: 1000, + offsetHeight: 500, + offsetTop: 3000, + offsetLeft: 0 + }, + { + offsetWidth: 1000, + offsetHeight: 501, + offsetTop: 3500, + offsetLeft: 0 + } + ], + endMargin: { + property: 'marginBottom', + value: 0 + } +} diff --git a/packages/embla-carousel/src/__tests__/slidesToScroll-ltr.test.ts b/packages/embla-carousel/src/__tests__/slidesToScroll-ltr.test.ts index 70b96713..70d5fe36 100644 --- a/packages/embla-carousel/src/__tests__/slidesToScroll-ltr.test.ts +++ b/packages/embla-carousel/src/__tests__/slidesToScroll-ltr.test.ts @@ -128,7 +128,7 @@ describe('➡️ SlidesToScroll - Horizontal LTR', () => { }) describe('"auto" is correct for slides WITHOUT MARGINS and:', () => { - describe('edge cases when slide is greater than viewport and:', () => { + describe('edge cases when slide width is greater than viewport and:', () => { const emblaApi = EmblaCarousel( mockTestElements(FIXTURE_SLIDES_TO_SCROLL_LTR_3) ) diff --git a/packages/embla-carousel/src/__tests__/slidesToScroll-rtl.test.ts b/packages/embla-carousel/src/__tests__/slidesToScroll-rtl.test.ts index 5d76f944..04836ac5 100644 --- a/packages/embla-carousel/src/__tests__/slidesToScroll-rtl.test.ts +++ b/packages/embla-carousel/src/__tests__/slidesToScroll-rtl.test.ts @@ -74,7 +74,7 @@ describe('➡️ SlidesToScroll - Horizontal RTL', () => { }) describe('"auto" is correct for slides WITHOUT MARGINS and:', () => { - describe('edge cases when slide is greater than viewport and:', () => { + describe('edge cases when slide width is greater than viewport and:', () => { const emblaApi = EmblaCarousel( mockTestElements(FIXTURE_SLIDES_TO_SCROLL_RTL_3) ) diff --git a/packages/embla-carousel/src/__tests__/slidesToScroll-vertical.test.ts b/packages/embla-carousel/src/__tests__/slidesToScroll-vertical.test.ts index b67b33af..9469720b 100644 --- a/packages/embla-carousel/src/__tests__/slidesToScroll-vertical.test.ts +++ b/packages/embla-carousel/src/__tests__/slidesToScroll-vertical.test.ts @@ -3,7 +3,8 @@ import { defaultOptions } from '../components/Options' import { mockTestElements } from './mocks' import { FIXTURE_SLIDES_TO_SCROLL_Y_1, - FIXTURE_SLIDES_TO_SCROLL_Y_2 + FIXTURE_SLIDES_TO_SCROLL_Y_2, + FIXTURE_SLIDES_TO_SCROLL_Y_3 } from './fixtures/slidesToScroll-vertical.fixture' const FIRST_SNAP_INDEX = 0 @@ -127,6 +128,101 @@ describe('➡️ SlidesToScroll - Vertical', () => { }) }) + describe('"auto" is correct for slides WITHOUT MARGINS and:', () => { + describe('edge cases when slide height is greater than viewport and:', () => { + const emblaApi = EmblaCarousel( + mockTestElements(FIXTURE_SLIDES_TO_SCROLL_Y_3) + ) + + beforeEach(() => { + emblaApi.reInit({ + ...defaultOptions, + slidesToScroll: 'auto', + axis: 'y' + }) + }) + + test('LOOP:FALSE', () => { + const engine = emblaApi.internalEngine() + const expectedScrollSnaps = [ + 0, -625, -1000, -1250, -1500, -1750, -2125, -2625, -3125, -3751 + ] + + expect(engine.scrollSnaps).toEqual(expectedScrollSnaps) + expect(engine.location.get()).toBe( + expectedScrollSnaps[FIRST_SNAP_INDEX] + ) + + expect(engine.slideRegistry).toEqual([ + [0], + [1], + [2], + [3], + [4], + [5], + [6], + [7], + [8], + [9] + ]) + }) + + test('LOOP:FALSE and CONTAINSCROLL:FALSE', () => { + emblaApi.reInit({ containScroll: false }) + + const engine = emblaApi.internalEngine() + const expectedScrollSnaps = [ + -125, -625, -1000, -1250, -1500, -1750, -2125, -2625, -3125, -3625.5 + ] + + expect(engine.scrollSnaps).toEqual(expectedScrollSnaps) + expect(engine.location.get()).toBe( + expectedScrollSnaps[FIRST_SNAP_INDEX] + ) + + expect(engine.slideRegistry).toEqual([ + [0], + [1], + [2], + [3], + [4], + [5], + [6], + [7], + [8], + [9] + ]) + }) + + test('LOOP:TRUE', () => { + emblaApi.reInit({ loop: true }) + + const engine = emblaApi.internalEngine() + const expectedScrollSnaps = [ + -125, -625, -1000, -1250, -1500, -1750, -2125, -2625, -3125, -3625.5 + ] + + expect(engine.scrollSnaps).toEqual(expectedScrollSnaps) + expect(engine.location.get()).toBe( + expectedScrollSnaps[FIRST_SNAP_INDEX] + ) + + expect(engine.slideRegistry).toEqual([ + [0], + [1], + [2], + [3], + [4], + [5], + [6], + [7], + [8], + [9] + ]) + }) + }) + }) + describe('"Custom number 2" is correct for slides WITHOUT MARGINS and:', () => { const emblaApi = EmblaCarousel( mockTestElements(FIXTURE_SLIDES_TO_SCROLL_Y_1)