Fix: Add logic to reset autoplay and autoscroll on carousel interaction - #164
Conversation
…n carousel interaction
There was a problem hiding this comment.
Pull request overview
Note
Copilot couldn't run its full agentic review because no GitHub Actions runner was available. Make sure your repository has a runner available to run Copilot's review, or add a copilot-setup-steps.yml file specifying one with the runs-on attribute. See the docs for more details.
Enhances carousel interaction handling so autoplay and autoScroll plugins are consistently stopped or reset when users use navigation controls, and adds tests to validate the behavior.
Changes:
- Added
stopPluginsOnInteractionutility to centralize stop/reset logic forautoplayandautoScroll. - Updated
scrollPrev,scrollNext, andscrollToSnapactions to invoke the utility before scrolling. - Added Jest tests for stop vs reset behavior based on
stopOnInteraction.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/blocks/carousel/view.ts | Centralizes plugin stop/reset behavior and wires it into scroll actions. |
| src/blocks/carousel/tests/view.test.ts | Adds tests verifying plugin stop/reset behavior on interaction. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…est cleanup with try-finally blocks
…g in autoplay and autoscroll plugins
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (5)
src/blocks/carousel/view.ts:134
- The function name
stopPluginsOnInteractionis misleading because it can alsoreset()plugins (i.e., not stop them). Consider renaming to something that reflects both behaviors (e.g.,handlePluginsOnInteraction/applyInteractionPolicyToPlugins) to reduce confusion for future maintainers.
const stopPluginsOnInteraction = (
src/blocks/carousel/tests/view.test.ts:351
- Given the new interaction logic in
stopPluginsOnInteraction, tests currently cover object configs withstopOnInteractiontrue/false/omitted, but they don’t cover the boolean config branches (context.autoplay === true/context.autoScroll === true) or the disabled cases (context.autoplay === false/context.autoScroll === false) with plugins present. Adding these cases would prevent regressions—especially around accidentally callingreset()when the feature is disabled.
it( 'should reset autoplay and autoscroll if stopOnInteraction is false', () => {
src/blocks/carousel/tests/view.test.ts:396
- Given the new interaction logic in
stopPluginsOnInteraction, tests currently cover object configs withstopOnInteractiontrue/false/omitted, but they don’t cover the boolean config branches (context.autoplay === true/context.autoScroll === true) or the disabled cases (context.autoplay === false/context.autoScroll === false) with plugins present. Adding these cases would prevent regressions—especially around accidentally callingreset()when the feature is disabled.
it( 'should stop (destroy) autoplay and autoscroll if stopOnInteraction is omitted/undefined (defaults to true)', () => {
src/blocks/carousel/view.ts:148
- The autoplay and autoScroll branches duplicate the same stop/destroy/reset control flow. Consider extracting a small helper (e.g.,
applyStopOnInteraction(plugin, enabledConfig)), or iterating over a list of plugin keys to keep the interaction policy consistent and reduce future drift when one branch changes and the other doesn’t.
if ( autoplay ) {
src/blocks/carousel/view.ts:165
- The autoplay and autoScroll branches duplicate the same stop/destroy/reset control flow. Consider extracting a small helper (e.g.,
applyStopOnInteraction(plugin, enabledConfig)), or iterating over a list of plugin keys to keep the interaction policy consistent and reduce future drift when one branch changes and the other doesn’t.
if ( autoScroll ) {
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (3)
src/blocks/carousel/view.ts:243
- The PR description mentions updating
scrollToSnapto invokestopPluginsOnInteraction, but the added tests only coverscrollPrev,scrollNext, andonDotClick. Please add coverage for thescrollToSnappath so regressions in this specific action (stop vs reset behavior, and destroy vs stop fallback) are caught.
const element = getElementRef( getElement() );
const embla = getEmblaFromElement( element );
if ( embla ) {
stopPluginsOnInteraction( embla, context );
if ( snap.index !== context.selectedIndex ) {
markForAnnouncement();
}
src/blocks/carousel/view.ts:180
- The stop/reset logic for
autoplayandautoScrollis effectively duplicated (including method preference order). Consider extracting a small helper (e.g.,handlePluginOnInteraction(plugin, shouldStop)) and a sharedshouldStopOnInteraction(config)computation. This reduces duplication, makes future plugin additions safer, and keeps behavior consistent across plugins.
const stopPluginsOnInteraction = (
embla: EmblaCarouselType,
context: CarouselContext,
): void => {
if ( typeof embla.plugins !== 'function' ) {
return;
}
const plugins = embla.plugins() as {
autoplay?: StoppablePlugin;
autoScroll?: StoppablePlugin;
};
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();
}
}
src/blocks/carousel/tests/view.test.ts:304
- The new tests repeat a lot of setup (DOM creation, Embla plugin mocks, context wiring, body append/remove). To keep the suite easier to extend, consider factoring an
arrange({ stopOnInteraction, omitDestroy, action })helper and/or usingit.eachfor the stop/reset permutations across actions.
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() };
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.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 );
}
} );
…autoscroll are disabled in context
|
@muralig-hub Can you please QA the PR when you get time |
Summary
This pull request enhances the carousel's behavior by improving how autoplay and autoscroll plugins respond to user interactions, and adds comprehensive tests to verify these behaviors. The main focus is to ensure that, depending on configuration, the carousel either stops or resets these plugins when users interact with navigation controls.
Carousel plugin interaction handling:
stopPluginsOnInteractionutility to centralize logic for stopping or resetting theautoplayandautoScrollplugins based on thestopOnInteractionconfiguration in the carousel context.scrollPrev,scrollNext, andscrollToSnapactions to invokestopPluginsOnInteractionbefore scrolling, ensuring consistent plugin behavior on user interaction. [1] [2] [3]Testing improvements:
autoplayandautoScrollplugins are properly stopped (destroyed) or reset when the user interacts with carousel controls, depending on thestopOnInteractionsetting.Type of change
Related issue(s)
Closes #163
What changed
Interaction and Stopandon Mouse Hoverare enabled the User is now able to interact with Next and previous buttons and carousel dots and stop autoscrollStop on Interactionis enabled, If user click on Next previous button or interact with slides it stops autoscrollon Mouse Hoveris enabled and if user hover on slides it stops the slide and On mouse out it continue with autoscroll effect.Breaking changes
Does this introduce a breaking change? If yes, describe the impact and migration path below.
Testing
Describe how this was tested.
Test details:
Screenshots / recordings
If applicable, add screenshots or short recordings.
Checklist