Skip to content

Instantly share code, notes, and snippets.

@willmanduffy
Created July 22, 2019 16:25
Show Gist options
  • Select an option

  • Save willmanduffy/33a708816e95044dabafcbfe9e3c7bb8 to your computer and use it in GitHub Desktop.

Select an option

Save willmanduffy/33a708816e95044dabafcbfe9e3c7bb8 to your computer and use it in GitHub Desktop.
import { mount } from 'enzyme';
import React from 'react';
import { mockAllIsIntersecting } from 'react-intersection-observer/test-utils';
import WithInfiniteScrolling from './with-infinite-scrolling';
describe('WithInfiniteScrolling', () => {
afterEach(() => {
jest.clearAllMocks();
});
const loadMoreFunction = jest.fn();
describe('when infinite scroll is not yet enabled', () => {
describe('when the collection has multiple pages', () => {
const wrapper = mount(
<WithInfiniteScrolling
hasMultiplePages={true}
loadMoreFunction={loadMoreFunction}
requireButtonClick={true}
>
<div id="very-cool-child" />
</WithInfiniteScrolling>
);
it('renders the child component', () => {
expect(wrapper.find('#very-cool-child')).toHaveLength(1);
});
it('does not render the beacon', () => {
expect(wrapper.find('#infinite-scroll-beacon')).toHaveLength(0);
});
describe('clicking the load more button', () => {
it('begins to load more', () => {
const subject = wrapper.find(
'#test-infinite-scroll-load-more-button'
);
subject.simulate('click');
expect(loadMoreFunction).toHaveBeenCalled();
});
});
});
describe('when the collection does not have multiple pages', () => {
const wrapper = mount(
<WithInfiniteScrolling
loadMoreFunction={loadMoreFunction}
hasMultiplePages={false}
requireButtonClick={true}
>
<div id="very-cool-child" />
</WithInfiniteScrolling>
);
it('should not display the load more button', () => {
const subject = wrapper.find('#test-infinite-scroll-load-more-button');
expect(subject).toHaveLength(0);
});
it('renders the child component', () => {
expect(wrapper.find('#very-cool-child')).toHaveLength(1);
});
});
});
describe('when infinite is enabled', () => {
describe('when the collection has multiple pages', () => {
describe('when not already loading more items from the collection', () => {
const wrapper = mount(
<WithInfiniteScrolling
hasMultiplePages={true}
loading={false}
loadMoreFunction={loadMoreFunction}
requireButtonClick={false}
>
<div id="very-cool-child" />
</WithInfiniteScrolling>
);
// FIXME: Having trouble with Enzyme and react-intersection-observer test utils
// https://github.com/thebuilder/react-intersection-observer/issues/241
it.skip('triggers loading more when the beacon is in view', () => {
const beacon = wrapper.find('#test-infinite-scroll-beacon');
mockAllIsIntersecting(true);
wrapper.update();
expect(loadMoreFunction).toHaveBeenCalled();
});
it('should not display the load more button anymore', () => {
const subject = wrapper.find(
'#test-infinite-scroll-load-more-button'
);
expect(subject).toHaveLength(0);
});
it('should display the loading signifier', () => {});
it('renders the child component', () => {
expect(wrapper.find('#very-cool-child')).toHaveLength(1);
});
});
describe('when already loading more items fom the collection', () => {
const wrapper = mount(
<WithInfiniteScrolling
hasMultiplePages={true}
loading={true}
loadMoreFunction={loadMoreFunction}
requireButtonClick={false}
>
<div id="very-cool-child" />
</WithInfiniteScrolling>
);
it('does nothing when the beacon is in view', () => {
mockAllIsIntersecting(true);
expect(loadMoreFunction).not.toHaveBeenCalled();
});
});
});
describe('when the collection does not have multiple pages', () => {
const wrapper = mount(
<WithInfiniteScrolling
loadMoreFunction={loadMoreFunction}
hasMultiplePages={false}
requireButtonClick={false}
>
<div id="very-cool-child" />
</WithInfiniteScrolling>
);
describe('when the beacon is in view', () => {
it('does nothing', () => {});
});
});
});
});
import React, { useEffect, useState } from 'react';
import { useInView } from 'react-intersection-observer';
const WithInfiniteScrolling = ({
children,
hasMultiplePages,
loading,
loadMoreFunction,
requireButtonClick = true
}) => {
const [infiniteDisabled, setInfiniteDisabled] = useState(requireButtonClick);
const [beaconRef, beaconInView] = useInView();
const eligibleToLoadMore = hasMultiplePages && !infiniteDisabled && !loading;
useEffect(() => {
if (eligibleToLoadMore && beaconInView) {
loadMoreFunction();
}
}, [eligibleToLoadMore, beaconInView]);
return (
<>
{children}
<div className="collection-frame-footer">
{hasMultiplePages && infiniteDisabled && !loading && (
<button
id="test-infinite-scroll-load-more-button"
onClick={() => {
setInfiniteDisabled(false);
loadMoreFunction();
}}
>
That's not enough
</button>
)}
{loading && <span>Loading...</span>}
{/* When this beacon comes into view (via IntersectionObserver) we will trigger the
loadMoreFunction. The absolute positioning / negative margin is to help deal with
latency as it will come into view before the bottom of the loaded collection. */}
{eligibleToLoadMore && (
<div
id="test-infinite-scroll-beacon"
ref={beaconRef}
style={{ position: 'absolute', marginTop: '-500px' }}
/>
)}
</div>
</>
);
};
export default WithInfiniteScrolling;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment