Skip to content

Commit

Permalink
feat(carousel): add individual item intervals
Browse files Browse the repository at this point in the history
Adds the ability to specify intervals per-item, which should
hopefully match the functionality of the upstream feature.

fixes #5305
  • Loading branch information
bpas247 committed Aug 7, 2020
1 parent d90ce3e commit d4e12f0
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 2 deletions.
13 changes: 11 additions & 2 deletions src/Carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ function CarouselFunc(uncontrolledProps: CarouselProps, ref) {
activeIndex || 0,
);

const intervals = useRef<number[]>([]);

if (!isSliding && activeIndex !== renderedActiveIndex) {
if (nextDirectionRef.current) {
setDirection(nextDirectionRef.current);
Expand Down Expand Up @@ -492,17 +494,23 @@ function CarouselFunc(uncontrolledProps: CarouselProps, ref) {
return undefined;
}

let chosenInterval = interval || undefined;

if (activeIndex !== undefined && intervals.current[activeIndex]) {
chosenInterval = intervals.current[activeIndex];
}

intervalHandleRef.current = window.setInterval(
document.visibilityState ? nextWhenVisible : next,
interval || undefined,
chosenInterval,
);

return () => {
if (intervalHandleRef.current !== null) {
clearInterval(intervalHandleRef.current);
}
};
}, [shouldPlay, next, interval, nextWhenVisible]);
}, [shouldPlay, next, activeIndex, interval, nextWhenVisible]);

const indicatorOnClicks = useMemo(
() =>
Expand Down Expand Up @@ -547,6 +555,7 @@ function CarouselFunc(uncontrolledProps: CarouselProps, ref) {
<div className={`${prefix}-inner`}>
{map(children, (child, index) => {
const isActive = index === renderedActiveIndex;
intervals.current[index] = child.props.interval as number;

return slide ? (
<Transition
Expand Down
33 changes: 33 additions & 0 deletions test/CarouselSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,39 @@ describe('<Carousel>', () => {
expect(onSelectSpy).to.have.been.calledOnce;
});

it('should go through the items given the specified intervals', () => {
const defaultInterval = 5000;
const intervals = [
1000,
500,
defaultInterval, // to test for no interval specified, it should be the default
];
const itemsWithIntervals = [
<Carousel.Item key={1} interval={intervals[0]}>
Item 1 content
</Carousel.Item>,
<Carousel.Item key={2} interval={intervals[1]}>
Item 2 content
</Carousel.Item>,
<Carousel.Item key={3}>Item 3 content</Carousel.Item>,
];

const onSelectSpy = sinon.spy();
mount(
<Carousel interval={defaultInterval} onSelect={onSelectSpy}>
{itemsWithIntervals}
</Carousel>,
);

const total = intervals.reduce((sum, current) => sum + current, 0);
clock.tick(total * 1.1);

expect(onSelectSpy).to.have.been.calledThrice;
expect(onSelectSpy.firstCall).to.have.been.calledWith(1);
expect(onSelectSpy.secondCall).to.have.been.calledWith(2);
expect(onSelectSpy.thirdCall).to.have.been.calledWith(0);
});

it('should stop going through items on hover and continue afterwards', () => {
const onSelectSpy = sinon.spy();
const interval = 500;
Expand Down
35 changes: 35 additions & 0 deletions www/src/examples/Carousel/IndividualIntervals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<Carousel>
<Carousel.Item interval={1000}>
<img
className="d-block w-100"
src="holder.js/800x400?text=First slide&bg=373940"
alt="First slide"
/>
<Carousel.Caption>
<h3>First slide label</h3>
<p>Nulla vitae elit libero, a pharetra augue mollis interdum.</p>
</Carousel.Caption>
</Carousel.Item>
<Carousel.Item interval={500}>
<img
className="d-block w-100"
src="holder.js/800x400?text=Second slide&bg=282c34"
alt="Third slide"
/>
<Carousel.Caption>
<h3>Second slide label</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</Carousel.Caption>
</Carousel.Item>
<Carousel.Item>
<img
className="d-block w-100"
src="holder.js/800x400?text=Third slide&bg=20232a"
alt="Third slide"
/>
<Carousel.Caption>
<h3>Third slide label</h3>
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur.</p>
</Carousel.Caption>
</Carousel.Item>
</Carousel>;
8 changes: 8 additions & 0 deletions www/src/pages/components/carousel.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import ComponentApi from '../../components/ComponentApi';
import ReactPlayground from '../../components/ReactPlayground';
import CarouselControlled from '../../examples/Carousel/Controlled';
import CarouselUncontrolled from '../../examples/Carousel/Uncontrolled';
import IndividualIntervals from '../../examples/Carousel/IndividualIntervals';

# Carousels

Expand All @@ -29,6 +30,13 @@ You can also _control_ the Carousel state, via the

<ReactPlayground codeText={CarouselControlled} />

## Individual Item Intervals

You can specify individual intervals for each carousel item via the `interval`
prop.

<ReactPlayground codeText={IndividualIntervals} />

## API

<ComponentApi metadata={props.data.carousel} />
Expand Down

0 comments on commit d4e12f0

Please sign in to comment.