Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
222 changes: 222 additions & 0 deletions src/blocks/carousel/__tests__/view.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,139 @@ describe( 'Carousel View Module', () => {

consoleSpy.mockRestore();
} );

it( 'should stop (destroy) autoplay and autoscroll if stopOnInteraction is true', () => {
const { wrapper, viewport, button } = createMockCarouselDOM();
const mockAutoplay = { stop: jest.fn(), destroy: jest.fn(), reset: jest.fn() };
const mockAutoScroll = { stop: jest.fn(), destroy: jest.fn(), reset: jest.fn() };
Comment on lines +263 to +264
const mockEmbla = createMockEmblaInstance( {
plugins: jest.fn( () => ( {
autoplay: mockAutoplay,
autoScroll: mockAutoScroll,
} ) ),
} );
Comment thread
milindmore22 marked this conversation as resolved.

setEmblaOnViewport( viewport, mockEmbla );

const mockContext = createMockContext( {
autoplay: {
delay: 3000,
stopOnInteraction: true,
stopOnMouseEnter: true,
},
autoScroll: {
speed: 1,
direction: 'forward',
startDelay: 0,
stopOnInteraction: true,
stopOnMouseEnter: true,
stopOnFocusIn: true,
},
} );

( getContext as jest.Mock ).mockReturnValue( mockContext );
( getElement as jest.Mock ).mockReturnValue( { ref: button } );
document.body.appendChild( wrapper );

try {
storeConfig.actions.scrollPrev();

expect( mockAutoplay.destroy ).toHaveBeenCalledTimes( 1 );
expect( mockAutoScroll.destroy ).toHaveBeenCalledTimes( 1 );
expect( mockAutoplay.reset ).not.toHaveBeenCalled();
expect( mockAutoScroll.reset ).not.toHaveBeenCalled();
} finally {
document.body.removeChild( wrapper );
}
} );

it( 'should reset autoplay and autoscroll if stopOnInteraction is false', () => {
const { wrapper, viewport, button } = createMockCarouselDOM();
const mockAutoplay = { stop: jest.fn(), destroy: jest.fn(), reset: jest.fn() };
const mockAutoScroll = { stop: jest.fn(), destroy: jest.fn(), reset: jest.fn() };
const mockEmbla = createMockEmblaInstance( {
plugins: jest.fn( () => ( {
autoplay: mockAutoplay,
autoScroll: mockAutoScroll,
} ) ),
} );

setEmblaOnViewport( viewport, mockEmbla );

const mockContext = createMockContext( {
autoplay: {
delay: 3000,
stopOnInteraction: false,
stopOnMouseEnter: true,
},
autoScroll: {
speed: 1,
direction: 'forward',
startDelay: 0,
stopOnInteraction: false,
stopOnMouseEnter: true,
stopOnFocusIn: true,
},
} );

( getContext as jest.Mock ).mockReturnValue( mockContext );
( getElement as jest.Mock ).mockReturnValue( { ref: button } );
document.body.appendChild( wrapper );

try {
storeConfig.actions.scrollPrev();

expect( mockAutoplay.destroy ).not.toHaveBeenCalled();
expect( mockAutoScroll.destroy ).not.toHaveBeenCalled();
expect( mockAutoplay.reset ).toHaveBeenCalledTimes( 1 );
expect( mockAutoScroll.reset ).toHaveBeenCalledTimes( 1 );
} finally {
document.body.removeChild( wrapper );
}
} );

it( 'should stop (destroy) autoplay and autoscroll if stopOnInteraction is omitted/undefined (defaults to true)', () => {
const { wrapper, viewport, button } = createMockCarouselDOM();
const mockAutoplay = { stop: jest.fn(), destroy: jest.fn(), reset: jest.fn() };
const mockAutoScroll = { stop: jest.fn(), destroy: jest.fn(), reset: jest.fn() };
const mockEmbla = createMockEmblaInstance( {
plugins: jest.fn( () => ( {
autoplay: mockAutoplay,
autoScroll: mockAutoScroll,
} ) ),
} );

setEmblaOnViewport( viewport, mockEmbla );

const mockContext = createMockContext( {
autoplay: {
delay: 3000,
// stopOnInteraction omitted
stopOnMouseEnter: true,
} as unknown as CarouselContext[ 'autoplay' ],
autoScroll: {
speed: 1,
direction: 'forward',
startDelay: 0,
// stopOnInteraction omitted
stopOnMouseEnter: true,
stopOnFocusIn: true,
} as unknown as CarouselContext[ 'autoScroll' ],
} );

( getContext as jest.Mock ).mockReturnValue( mockContext );
( getElement as jest.Mock ).mockReturnValue( { ref: button } );
document.body.appendChild( wrapper );

try {
storeConfig.actions.scrollPrev();

expect( mockAutoplay.destroy ).toHaveBeenCalledTimes( 1 );
expect( mockAutoScroll.destroy ).toHaveBeenCalledTimes( 1 );
} finally {
document.body.removeChild( wrapper );
}
} );
} );

describe( 'scrollNext', () => {
Expand Down Expand Up @@ -295,6 +428,49 @@ describe( 'Carousel View Module', () => {

consoleSpy.mockRestore();
} );

it( 'should stop autoplay and autoscroll if stopOnInteraction is true', () => {
const { wrapper, viewport, button } = createMockCarouselDOM();
const mockAutoplay = { stop: jest.fn(), destroy: jest.fn(), reset: jest.fn() };
const mockAutoScroll = { stop: jest.fn(), destroy: jest.fn(), reset: jest.fn() };
const mockEmbla = createMockEmblaInstance( {
plugins: jest.fn( () => ( {
autoplay: mockAutoplay,
autoScroll: mockAutoScroll,
} ) ),
} );

setEmblaOnViewport( viewport, mockEmbla );

const mockContext = createMockContext( {
autoplay: {
delay: 3000,
stopOnInteraction: true,
stopOnMouseEnter: true,
},
autoScroll: {
speed: 1,
direction: 'forward',
startDelay: 0,
stopOnInteraction: true,
stopOnMouseEnter: true,
stopOnFocusIn: true,
},
} );

( getContext as jest.Mock ).mockReturnValue( mockContext );
( getElement as jest.Mock ).mockReturnValue( { ref: button } );
document.body.appendChild( wrapper );

try {
storeConfig.actions.scrollNext();

expect( mockAutoplay.destroy ).toHaveBeenCalledTimes( 1 );
expect( mockAutoScroll.destroy ).toHaveBeenCalledTimes( 1 );
} finally {
document.body.removeChild( wrapper );
}
} );
} );

describe( 'onDotClick', () => {
Expand Down Expand Up @@ -356,6 +532,52 @@ describe( 'Carousel View Module', () => {

document.body.removeChild( wrapper );
} );

it( 'should stop autoplay and autoscroll if stopOnInteraction is true', () => {
const { wrapper, viewport, button } = createMockCarouselDOM();
const mockAutoplay = { stop: jest.fn(), destroy: jest.fn(), reset: jest.fn() };
const mockAutoScroll = { stop: jest.fn(), destroy: jest.fn(), reset: jest.fn() };
const mockEmbla = createMockEmblaInstance( {
plugins: jest.fn( () => ( {
autoplay: mockAutoplay,
autoScroll: mockAutoScroll,
} ) ),
} );

setEmblaOnViewport( viewport, mockEmbla );

const mockContext = createMockContext( {
autoplay: {
delay: 3000,
stopOnInteraction: true,
stopOnMouseEnter: true,
},
autoScroll: {
speed: 1,
direction: 'forward',
startDelay: 0,
stopOnInteraction: true,
stopOnMouseEnter: true,
stopOnFocusIn: true,
},
} );
( mockContext as CarouselContext & { snap?: { index: number } } ).snap = {
index: 2,
};

( getContext as jest.Mock ).mockReturnValue( mockContext );
( getElement as jest.Mock ).mockReturnValue( { ref: button } );
document.body.appendChild( wrapper );

try {
storeConfig.actions.onDotClick();

expect( mockAutoplay.destroy ).toHaveBeenCalledTimes( 1 );
expect( mockAutoScroll.destroy ).toHaveBeenCalledTimes( 1 );
} finally {
document.body.removeChild( wrapper );
}
} );
} );
} );

Expand Down
63 changes: 63 additions & 0 deletions src/blocks/carousel/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,61 @@ const markForAnnouncement = (): void => {
getContext<CarouselContext>().shouldAnnounce = true;
};

type StoppablePlugin = {
stop?: () => void;
destroy?: () => void;
reset?: () => void;
};

const stopPluginsOnInteraction = (
embla: EmblaCarouselType,
context: CarouselContext,
): void => {
if ( typeof embla.plugins !== 'function' ) {
return;
}
const plugins = embla.plugins() as {
autoplay?: StoppablePlugin;
autoScroll?: StoppablePlugin;
};
Comment thread
milindmore22 marked this conversation as resolved.
const autoplay = plugins.autoplay;
const autoScroll = plugins.autoScroll;

if ( autoplay ) {
const shouldStop =
context.autoplay === true ||
( typeof context.autoplay === 'object' &&
context.autoplay.stopOnInteraction !== false );

if ( shouldStop ) {
if ( typeof autoplay.destroy === 'function' ) {
autoplay.destroy();
} else if ( typeof autoplay.stop === 'function' ) {
autoplay.stop();
}
} else if ( typeof autoplay.reset === 'function' ) {
autoplay.reset();
}
}

if ( autoScroll ) {
const shouldStop =
context.autoScroll === true ||
( typeof context.autoScroll === 'object' &&
context.autoScroll.stopOnInteraction !== false );

if ( shouldStop ) {
if ( typeof autoScroll.destroy === 'function' ) {
autoScroll.destroy();
} else if ( typeof autoScroll.stop === 'function' ) {
autoScroll.stop();
}
} else if ( typeof autoScroll.reset === 'function' ) {
autoScroll.reset();
}
}
};

store( 'rt-carousel/carousel', {
state: {
get canScrollPrev() {
Expand All @@ -141,6 +196,9 @@ store( 'rt-carousel/carousel', {
const element = getElementRef( getElement() );
const embla = getEmblaFromElement( element );
if ( embla ) {
const context = getContext<CarouselContext>();
stopPluginsOnInteraction( embla, context );

if ( embla.canScrollPrev() ) {
markForAnnouncement();
}
Expand All @@ -154,6 +212,9 @@ store( 'rt-carousel/carousel', {
const element = getElementRef( getElement() );
const embla = getEmblaFromElement( element );
if ( embla ) {
const context = getContext<CarouselContext>();
stopPluginsOnInteraction( embla, context );

if ( embla.canScrollNext() ) {
markForAnnouncement();
}
Expand All @@ -173,6 +234,8 @@ store( 'rt-carousel/carousel', {
const element = getElementRef( getElement() );
const embla = getEmblaFromElement( element );
if ( embla ) {
stopPluginsOnInteraction( embla, context );

if ( snap.index !== context.selectedIndex ) {
markForAnnouncement();
}
Expand Down
Loading