Unverified Commit 86ff1955 authored by BM's avatar BM Committed by GitHub
Browse files

fix(Carousel): support for ref attribute (#4917)



* fix(Carousel): support for ref attribute

* fix(Carousel): revisions to ref attribute

* fix(Carousel): add imperative logic to expose refs

* refactor: Revise implementation to use hooks more

* simplify

* fix callbacks

* Apply suggestions from code review

Co-authored-by: default avatarmuzakparov <b.muzakparov@newagesol.com>
Co-authored-by: default avatarJimmy Jia <tesrin@gmail.com>
parent fac2160c
This diff is collapsed.
......@@ -68,17 +68,17 @@ describe('<Carousel>', () => {
.simulate('click');
});
it('should call onSelect with previous direction and event', done => {
function onSelect(index, direction, event) {
['onSlide', 'onSlid'].forEach(eventName => {
it(`should call ${eventName} with previous index and direction`, done => {
function onEvent(index, direction) {
expect(index).to.equal(0);
expect(direction).to.equal('prev');
expect(event).to.exist;
expect(direction).to.equal('right');
done();
}
const wrapper = mount(
<Carousel activeIndex={1} onSelect={onSelect}>
<Carousel defaultActiveIndex={1} {...{ [eventName]: onEvent }}>
{items}
</Carousel>,
);
......@@ -89,18 +89,17 @@ describe('<Carousel>', () => {
.simulate('click');
});
it('should call onSelect with next direction and event', done => {
function onSelect(index, direction, event) {
it(`should call ${eventName} with next index and direction`, done => {
function onEvent(index, direction) {
const lastPossibleIndex = items.length - 1;
expect(index).to.equal(lastPossibleIndex);
expect(direction).to.equal('next');
expect(event).to.exist;
expect(direction).to.equal('left');
done();
}
const wrapper = mount(
<Carousel activeIndex={1} onSelect={onSelect}>
<Carousel defaultActiveIndex={1} {...{ [eventName]: onEvent }}>
{items}
</Carousel>,
);
......@@ -110,6 +109,7 @@ describe('<Carousel>', () => {
.last()
.simulate('click');
});
});
describe('Buttons and labels with and without wrapping', () => {
it('should show back button control on the first image if wrap is true', () => {
......@@ -272,7 +272,7 @@ describe('<Carousel>', () => {
{items}
</Carousel>,
);
clock.tick(interval * 2);
clock.tick(interval * 1.5);
expect(onSelectSpy).to.have.been.calledOnce;
});
......@@ -285,11 +285,11 @@ describe('<Carousel>', () => {
</Carousel>,
);
wrapper.simulate('mouseOver');
clock.tick(interval * 2);
clock.tick(interval * 1.5);
sinon.assert.notCalled(onSelectSpy);
wrapper.simulate('mouseOut');
clock.tick(interval * 2);
clock.tick(interval * 1.5);
sinon.assert.calledOnce(onSelectSpy);
});
......@@ -297,17 +297,13 @@ describe('<Carousel>', () => {
const onSelectSpy = sinon.spy();
const interval = 500;
const wrapper = mount(
<Carousel
interval={interval}
onSelect={onSelectSpy}
pauseOnHover={false}
>
<Carousel interval={interval} onSelect={onSelectSpy} pause={false}>
{items}
</Carousel>,
);
wrapper.simulate('mouseOver');
clock.tick(interval * 2);
clock.tick(interval * 1.5);
expect(onSelectSpy).to.have.been.calledOnce;
});
......@@ -320,7 +316,7 @@ describe('<Carousel>', () => {
</Carousel>,
);
wrapper.unmount();
clock.tick(interval * 2);
clock.tick(interval * 1.5);
expect(onSelectSpy).not.to.have.been.called;
});
});
......@@ -340,7 +336,7 @@ describe('<Carousel>', () => {
const onSelectSpy = sinon.spy();
const wrapper = mount(
<Carousel activeIndex={0} interval={0} onSelect={onSelectSpy}>
<Carousel activeIndex={0} onSelect={onSelectSpy}>
{items}
</Carousel>,
);
......@@ -349,19 +345,15 @@ describe('<Carousel>', () => {
key: 'ArrowLeft',
});
clock.tick(50);
sinon.assert.calledOnce(onSelectSpy);
sinon.assert.calledWith(onSelectSpy, items.length - 1);
expect(onSelectSpy).to.have.been.calledOnceWith(items.length - 1);
});
it('should wrap from first to last', () => {
const onSelectSpy = sinon.spy();
const wrapper = mount(
<Carousel
activeIndex={items.length - 1}
interval={0}
onSelect={onSelectSpy}
>
<Carousel activeIndex={items.length - 1} onSelect={onSelectSpy}>
{items}
</Carousel>,
);
......@@ -370,8 +362,8 @@ describe('<Carousel>', () => {
key: 'ArrowRight',
});
clock.tick(50);
sinon.assert.calledOnce(onSelectSpy);
sinon.assert.calledWith(onSelectSpy, 0);
expect(onSelectSpy).to.have.been.calledOnceWith(0);
});
[
......@@ -396,7 +388,6 @@ describe('<Carousel>', () => {
<Carousel
activeIndex={activeIndex}
wrap={false}
interval={0}
onSelect={onSelectSpy}
>
{items}
......@@ -415,6 +406,7 @@ describe('<Carousel>', () => {
describe('keyboard events', () => {
let clock;
beforeEach(() => {
clock = sinon.useFakeTimers();
});
......@@ -426,7 +418,7 @@ describe('<Carousel>', () => {
it('should go back for the keyboard event ArrowLeft', () => {
const onSelectSpy = sinon.spy();
const wrapper = mount(
<Carousel activeIndex={1} interval={0} onSelect={onSelectSpy}>
<Carousel activeIndex={1} onSelect={onSelectSpy}>
{items}
</Carousel>,
);
......@@ -442,7 +434,7 @@ describe('<Carousel>', () => {
it('should go forward for the keyboard event ArrowRight', () => {
const onSelectSpy = sinon.spy();
const wrapper = mount(
<Carousel activeIndex={1} interval={0} onSelect={onSelectSpy}>
<Carousel activeIndex={1} onSelect={onSelectSpy}>
{items}
</Carousel>,
);
......@@ -458,12 +450,7 @@ describe('<Carousel>', () => {
it('should ignore keyEvents when the keyboard is disabled', () => {
const onSelectSpy = sinon.spy();
const wrapper = mount(
<Carousel
activeIndex={1}
interval={0}
onSelect={onSelectSpy}
keyboard={false}
>
<Carousel activeIndex={1} onSelect={onSelectSpy} keyboard={false}>
{items}
</Carousel>,
);
......@@ -475,11 +462,11 @@ describe('<Carousel>', () => {
sinon.assert.notCalled(onSelectSpy);
});
['ArrowUp', 'ArrowRightLeft', 'Onwards'].forEach(({ key }) => {
['ArrowUp', 'ArrowRightLeft', 'Onwards'].forEach(key => {
it('should do nothing for non left or right keys', () => {
const onSelectSpy = sinon.spy();
const wrapper = mount(
<Carousel activeIndex={1} interval={0} onSelect={onSelectSpy}>
<Carousel activeIndex={1} onSelect={onSelectSpy}>
{items}
</Carousel>,
);
......@@ -509,40 +496,39 @@ describe('<Carousel>', () => {
afterEach(() => {
clock.restore();
clock.tick(150);
});
it('should swipe right', () => {
wrapper.simulate('touchStart', { changedTouches: [{ screenX: 50 }] });
wrapper.simulate('touchEnd', { changedTouches: [{ screenX: 0 }] });
wrapper.simulate('touchStart', { touches: [{ clientX: 50 }] });
wrapper.simulate('touchMove', { touches: [{ clientX: 0 }] });
wrapper.simulate('touchEnd');
clock.tick(50);
expect(onSelectSpy).to.have.been.calledOnce;
expect(onSelectSpy.getCall(0).args[0]).to.equal(2);
expect(onSelectSpy).to.have.been.calledOnceWith(2);
});
it('should swipe left', () => {
wrapper.simulate('touchStart', { changedTouches: [{ screenX: 0 }] });
wrapper.simulate('touchEnd', { changedTouches: [{ screenX: 50 }] });
wrapper.simulate('touchStart', { touches: [{ clientX: 0 }] });
wrapper.simulate('touchMove', { touches: [{ clientX: 50 }] });
wrapper.simulate('touchEnd');
clock.tick(50);
expect(onSelectSpy).to.have.been.calledOnce;
expect(onSelectSpy.getCall(0).args[0]).to.equal(0);
expect(onSelectSpy).to.have.been.calledOnceWith(0);
});
it('should not swipe if swipe detected is under the swipe threshold', () => {
const SWIPE_THRESHOLD = 40;
wrapper.simulate('touchStart', { changedTouches: [{ screenX: 0 }] });
wrapper.simulate('touchEnd', {
changedTouches: [{ screenX: SWIPE_THRESHOLD - 5 }],
});
wrapper.simulate('touchStart', { touches: [{ clientX: 0 }] });
wrapper.simulate('touchMove', { touches: [{ clientX: 35 }] });
wrapper.simulate('touchEnd');
clock.tick(50);
expect(onSelectSpy).to.not.have.been.called;
});
it('should do nothing with disabled touch right', () => {
const noTochWrapper = mount(
const noTouchWrapper = mount(
<Carousel
activeIndex={1}
interval={null}
......@@ -552,12 +538,13 @@ describe('<Carousel>', () => {
{items}
</Carousel>,
);
noTochWrapper.simulate('touchStart', {
changedTouches: [{ screenX: 50 }],
noTouchWrapper.simulate('touchStart', {
touches: [{ clientX: 50 }],
});
noTochWrapper.simulate('touchEnd', {
changedTouches: [{ screenX: 0 }],
noTouchWrapper.simulate('touchMove', {
touches: [{ clientX: 0 }],
});
noTouchWrapper.simulate('touchEnd');
clock.tick(50);
expect(onSelectSpy).to.not.have.been.called;
......
......@@ -9,20 +9,22 @@ export interface CarouselProps {
bsPrefix?: string;
slide?: boolean;
fade?: boolean;
wrap?: boolean;
controls?: boolean;
indicators?: boolean;
activeIndex?: number;
onSelect?: (eventKey: number, event: object | null) => void;
defaultActiveIndex?: number;
onSlide?: (eventKey: number, direction: 'left' | 'right') => void;
onSlid?: (eventKey: number, direction: 'left' | 'right') => void;
interval?: number | null;
controls?: boolean;
pauseOnHover?: boolean;
keyboard?: boolean;
onSelect?: (eventKey: any, direction: 'prev' | 'next', event: object) => void;
onSlideEnd?: () => void;
activeIndex?: number;
pause?: 'hover' | false;
wrap?: boolean;
touch?: boolean;
prevIcon?: React.ReactNode;
prevLabel?: string;
prevLabel?: React.ReactNode;
nextIcon?: React.ReactNode;
nextLabel?: string;
touch?: boolean;
nextLabel?: React.ReactNode;
}
declare class Carousel<
......
......@@ -951,7 +951,7 @@
resolved "https://registry.yarnpkg.com/@restart/context/-/context-2.1.4.tgz#a99d87c299a34c28bd85bb489cb07bfd23149c02"
integrity sha512-INJYZQJP7g+IoDUh/475NlGiTeMfwTXUEr3tmRneckHIxNolGOW9CTq83S8cxq0CgJwwcMzMJFchxvlwe7Rk8Q==
"@restart/hooks@^0.3.11", "@restart/hooks@^0.3.12":
"@restart/hooks@^0.3.12", "@restart/hooks@^0.3.21":
version "0.3.21"
resolved "https://registry.yarnpkg.com/@restart/hooks/-/hooks-0.3.21.tgz#5264d12019ffb844dc1fc44d55517ded7b580ee2"
integrity sha512-Wcu3CFJV+iiqPEIoPVx3/CYnZBRgPeRABo6bLJByRH9ptJXyObn7WYPG7Rv0cg3+55bqcBbG0xEfovzwE2PNXg==
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment