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
41 changes: 41 additions & 0 deletions src/blocks/carousel/__tests__/edit.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ describe( 'Carousel Edit setup flow', () => {
expect( selectLabels ).not.toContain( 'Contain Scroll' );
expect( toggleLabels ).not.toContain( 'Free Drag' );
expect( toggleLabels ).not.toContain( 'Scroll Auto' );
expect( toggleLabels ).toContain( 'Enable Auto Scroll' );
expect( ( RangeControl as unknown as jest.Mock ).mock.calls.some(
( [ props ] ) => props.label === 'Slides to Scroll',
) ).toBe( false );
Expand All @@ -282,6 +283,7 @@ describe( 'Carousel Edit setup flow', () => {
expect( selectLabels ).toContain( 'Contain Scroll' );
expect( toggleLabels ).toContain( 'Free Drag' );
expect( toggleLabels ).toContain( 'Scroll Auto' );
expect( toggleLabels ).toContain( 'Enable Auto Scroll' );
} );

it( 'calls setAttributes with the selected transition', () => {
Expand All @@ -302,4 +304,43 @@ describe( 'Carousel Edit setup flow', () => {

expect( setAttributes ).toHaveBeenCalledWith( { transition: 'fade' } );
} );

it( 'disables the transition dropdown with notice when autoScroll is enabled', () => {
render(
<Edit
attributes={ { ...createAttributes(), autoScroll: true } }
setAttributes={ jest.fn() }
clientId="client-transition-disabled"
/>,
);

const transitionCall = ( SelectControl as unknown as jest.Mock ).mock.calls.find(
( [ props ] ) => props.label === 'Transition',
);

expect( transitionCall[ 0 ].disabled ).toBe( true );
expect( transitionCall[ 0 ].help ).toBe( 'Auto Scroll does not support transitions.' );
} );

it( 'switches transition to slide when autoScroll is enabled', () => {
const setAttributes = jest.fn();
render(
<Edit
attributes={ { ...createAttributes(), transition: 'fade' } }
setAttributes={ setAttributes }
clientId="client-autoscroll-toggle"
/>,
);

const autoScrollToggle = ( ToggleControl as unknown as jest.Mock ).mock.calls.find(
( [ props ] ) => props.label === 'Enable Auto Scroll',
);

autoScrollToggle[ 0 ].onChange( true );

expect( setAttributes ).toHaveBeenCalledWith( expect.objectContaining( {
autoScroll: true,
transition: 'slide',
} ) );
} );
} );
97 changes: 97 additions & 0 deletions src/blocks/carousel/__tests__/view.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { store, getContext, getElement } from '@wordpress/interactivity';

import EmblaCarousel, { type EmblaCarouselType } from 'embla-carousel';
import Fade from 'embla-carousel-fade';
import AutoScroll from 'embla-carousel-auto-scroll';

// Symbol key used by the view.ts for Embla instances
const EMBLA_KEY = Symbol.for( 'rt-carousel.carousel' );
Expand Down Expand Up @@ -174,6 +175,29 @@ describe( 'Carousel View Module', () => {

expect( result ).toBe( false );
} );

it( 'should return true when autoScroll is enabled', () => {
const mockContext = createMockContext( {
canScrollPrev: false,
autoScroll: {
speed: 2,
direction: 'forward',
startDelay: 1000,
stopOnInteraction: true,
stopOnMouseEnter: false,
stopOnFocusIn: true,
},
} );
( getContext as jest.Mock ).mockReturnValue( mockContext );

const result =
Object.getOwnPropertyDescriptor(
storeConfig.state,
'canScrollPrev',
)?.get?.() ?? false;

expect( result ).toBe( true );
} );
} );

describe( 'canScrollNext', () => {
Expand Down Expand Up @@ -202,6 +226,29 @@ describe( 'Carousel View Module', () => {

expect( result ).toBe( false );
} );

it( 'should return true when autoScroll is enabled', () => {
const mockContext = createMockContext( {
canScrollNext: false,
autoScroll: {
speed: 2,
direction: 'forward',
startDelay: 1000,
stopOnInteraction: true,
stopOnMouseEnter: false,
stopOnFocusIn: true,
},
} );
( getContext as jest.Mock ).mockReturnValue( mockContext );

const result =
Object.getOwnPropertyDescriptor(
storeConfig.state,
'canScrollNext',
)?.get?.() ?? false;

expect( result ).toBe( true );
} );
} );
} );

Expand Down Expand Up @@ -913,6 +960,56 @@ describe( 'Carousel View Module', () => {
originalIntersectionObserver;
}
} );

it( 'includes the AutoScroll plugin when transition is fade', () => {
const mockContext = createMockContext( {
transition: 'fade',
autoScroll: {
speed: 2,
direction: 'forward',
startDelay: 1000,
stopOnInteraction: true,
stopOnMouseEnter: false,
stopOnFocusIn: true,
},
} );
Comment on lines +964 to +975
Comment on lines +964 to +975
const { wrapper, viewport } = createMockCarouselDOM();
const originalIntersectionObserver = window.IntersectionObserver;

viewport.getBoundingClientRect = jest.fn( () => ( {
width: 100,
height: 0,
top: 0,
right: 0,
bottom: 0,
left: 0,
x: 0,
y: 0,
toJSON: () => ( {} ),
} ) );

( getContext as jest.Mock ).mockReturnValue( mockContext );
( getElement as jest.Mock ).mockReturnValue( { ref: wrapper } );
( EmblaCarousel as unknown as jest.Mock ).mockReturnValue(
createMockEmblaInstance( {
scrollProgress: jest.fn( () => 0 ),
slideNodes: jest.fn( () => [] ),
} ),
);
delete ( window as Window & { IntersectionObserver?: typeof IntersectionObserver } ).IntersectionObserver;

try {
storeConfig.callbacks.initCarousel();

const lastCall = ( EmblaCarousel as unknown as jest.Mock ).mock.calls.at( -1 );
expect( lastCall?.[ 2 ] ).toContainEqual( ( Fade as unknown as jest.Mock ).mock.results.at( -1 )?.value );
const autoScrollMockResult = ( AutoScroll as unknown as jest.Mock ).mock.results.at( -1 )?.value;
expect( lastCall?.[ 2 ] ).toContainEqual( autoScrollMockResult );
Comment on lines +1005 to +1007
} finally {
Comment on lines +1006 to +1008
( window as Window & { IntersectionObserver?: typeof IntersectionObserver } ).IntersectionObserver =
originalIntersectionObserver;
}
} );
} );
} );
} );
Expand Down
5 changes: 3 additions & 2 deletions src/blocks/carousel/controls/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export default function Edit() {
emblaApi: contextApi,
canScrollPrev,
canScrollNext,
carouselOptions,
} = useContext( EditorCarouselContext );
Comment on lines 13 to 17
Comment on lines 13 to 17
const ref = useRef<HTMLDivElement>( null );

Expand Down Expand Up @@ -58,7 +59,7 @@ export default function Edit() {
handleScroll( 'prev' );
} }
type="button"
disabled={ ! canScrollPrev }
disabled={ ! carouselOptions?.autoScroll && ! canScrollPrev }
aria-label={ __( 'Previous Slide', 'rt-carousel' ) }
>
<PreviousIcon />
Expand All @@ -70,7 +71,7 @@ export default function Edit() {
handleScroll( 'next' );
} }
type="button"
disabled={ ! canScrollNext }
disabled={ ! carouselOptions?.autoScroll && ! canScrollNext }
aria-label={ __( 'Next Slide', 'rt-carousel' ) }
>
<NextIcon />
Expand Down
29 changes: 20 additions & 9 deletions src/blocks/carousel/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -322,17 +322,22 @@ export default function Edit( {
<SelectControl
label={ __( 'Transition', 'rt-carousel' ) }
value={ transition }
disabled={ autoScroll }
Comment on lines 322 to +325
Comment on lines 322 to +325
options={ [
{ label: __( 'Slide', 'rt-carousel' ), value: 'slide' },
{ label: __( 'Fade', 'rt-carousel' ), value: 'fade' },
] }
onChange={ ( value ) =>
setAttributes( { transition: value as CarouselAttributes[ 'transition' ] } )
}
help={ __(
'Choose how slides transition: sliding horizontally or cross-fading.',
'rt-carousel',
) }
help={
autoScroll
? __( 'Auto Scroll does not support transitions.', 'rt-carousel' )
: __(
'Choose how slides transition: sliding horizontally or cross-fading.',
'rt-carousel',
)
}
Comment on lines 330 to +340
Comment on lines +333 to +340
/>
<ToggleControl
label={ __( 'Loop', 'rt-carousel' ) }
Expand Down Expand Up @@ -503,11 +508,17 @@ export default function Edit( {
<ToggleControl
label={ __( 'Enable Auto Scroll', 'rt-carousel' ) }
checked={ autoScroll }
onChange={ ( value ) => setAttributes( {
autoScroll: value,
autoplay: value ? false : autoplay,
loop: ( value && autoScrollDirection === 'backward' ) ? true : loop,
} ) }
onChange={ ( value ) => {
const nextAttributes: Partial< CarouselAttributes > = {
autoScroll: value,
autoplay: value ? false : autoplay,
loop: ( value && autoScrollDirection === 'backward' ) ? true : loop,
};
if ( value ) {
nextAttributes.transition = 'slide';
}
setAttributes( nextAttributes );
} }
Comment on lines 508 to +521
/>
{ autoScroll && ( <>
<RangeControl
Expand Down
4 changes: 2 additions & 2 deletions src/blocks/carousel/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,11 @@ store( 'rt-carousel/carousel', {
state: {
get canScrollPrev() {
const context = getContext<CarouselContext>();
return context.canScrollPrev;
return context.autoScroll ? true : context.canScrollPrev;
},
get canScrollNext() {
const context = getContext<CarouselContext>();
return context.canScrollNext;
return context.autoScroll ? true : context.canScrollNext;
},
},
actions: {
Expand Down
16 changes: 16 additions & 0 deletions tests/js/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,22 @@ jest.mock( 'embla-carousel-fade', () => ( {
} ) ),
} ) );

/**
* Mock embla-carousel-auto-scroll module.
*/
jest.mock( 'embla-carousel-auto-scroll', () => ( {
__esModule: true,
default: jest.fn( () => ( {
name: 'autoScroll',
options: {},
init: jest.fn(),
destroy: jest.fn(),
play: jest.fn(),
stop: jest.fn(),
isPlaying: jest.fn( () => false ),
} ) ),
} ) );
Comment on lines +67 to +78

/**
* Mock WordPress block editor components.
*/
Expand Down
Loading