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
9 changes: 6 additions & 3 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"files": [
"README.md"
],
"imageSize": 100,
"contributorsPerLine": 5,
"imageSize": 80,
"contributorsPerLine": 6,
"skipCi": true,
"contributors": [
{
Expand All @@ -25,7 +25,10 @@
"avatar_url": "https://avatars.githubusercontent.com/u/25670841?v=4",
"profile": "https://github.com/iamdarshshah",
"contributions": [
"infra"
"infra",
"code",
"review",
"maintenance"
]
}
],
Expand Down
22 changes: 17 additions & 5 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,27 @@ module.exports = {
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'prettier',
'prettier/@typescript-eslint',
'prettier/react',
],
settings: {
react: {
version: '17.0',
},
},
rules: {
'@typescript-eslint/prefer-regexp-exec': 1,
'@typescript-eslint/ban-ts-ignore': 0,
'@typescript-eslint/unbound-method': 1,
'@typescript-eslint/prefer-regexp-exec': 'warn',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/unbound-method': 'warn',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-expressions': 'warn',
'@typescript-eslint/no-unsafe-return': 'warn',
'@typescript-eslint/no-unsafe-assignment': 'warn',
'@typescript-eslint/no-unsafe-call': 'warn',
'@typescript-eslint/no-unsafe-argument': 'warn',
'@typescript-eslint/no-empty-object-type': 'warn',
'react/no-deprecated': 'warn',
},
};
2 changes: 1 addition & 1 deletion .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: [16.x, 18.x, 20.x]
node-version: [18.x, 20.x, 22.x]

steps:
- uses: actions/checkout@v4
Expand Down
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
yarn run ts-check && npx lint-staged
12 changes: 12 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,18 @@ module.exports = {
// A preset that is used as a base for Jest's configuration
preset: 'ts-jest',

// ts-jest configuration for React 17 JSX transform
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
tsconfig: {
jsx: 'react-jsx',
},
},
],
},

// Run tests from one or more projects
// projects: null,

Expand Down
66 changes: 30 additions & 36 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"name": "react-infinite-scroll-component",
"version": "6.1.1",
"version": "7.0.0",
"description": "An Infinite Scroll component in react.",
"engines": {
"node": ">=18.18.0"
},
"source": "src/index.tsx",
"main": "dist/index.js",
"unpkg": "dist/index.umd.js",
Expand All @@ -18,7 +21,8 @@
"prettier:check": "prettier --check 'src/**/*'",
"prettify": "prettier --write 'src/**/*'",
"ts-check": "tsc -p tsconfig.json --noEmit",
"test": "jest"
"test": "jest",
"prepare": "husky"
},
"repository": {
"type": "git",
Expand All @@ -39,64 +43,54 @@
},
"homepage": "https://github.com/ankeetmaini/react-infinite-scroll-component#readme",
"peerDependencies": {
"react": ">=16.0.0"
"react": "^17.0.0",
"react-dom": "^17.0.0"
},
"devDependencies": {
"@babel/core": "^7.6.2",
"@babel/core": "^7.20.0",
"@storybook/addon-actions": "^5.2.1",
"@storybook/addon-info": "^5.2.1",
"@storybook/addon-links": "^5.2.1",
"@storybook/addons": "^5.2.1",
"@storybook/react": "^5.2.1",
"@testing-library/react": "^9.2.0",
"@types/jest": "^24.0.18",
"@types/react": "^16.9.2",
"@types/react-dom": "^16.9.1",
"@storybook/react": "^7.0.0",
"@testing-library/react": "^12.1.5",
"@types/jest": "^29.5.14",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@types/storybook__react": "^4.0.2",
"@types/throttle-debounce": "^2.1.0",
"@typescript-eslint/eslint-plugin": "^2.3.2",
"@typescript-eslint/parser": "^2.3.2",
"@typescript-eslint/eslint-plugin": "^8.50.1",
"@typescript-eslint/parser": "^8.50.1",
"awesome-typescript-loader": "^5.2.1",
"babel-loader": "^8.0.6",
"eslint": "^6.5.1",
"eslint-config-prettier": "^6.3.0",
"eslint-plugin-react": "^7.15.0",
"husky": ">=1",
"jest": "^24.9.0",
"lint-staged": ">=8",
"prettier": "1.18.2",
"react": "^16.10.1",
"eslint": "^8",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-react": "^7.37.5",
"husky": "^9.0.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"lint-staged": "^15.0.0",
"prettier": "^2.8.0",
"react": "^17.0.2",
"react-docgen-typescript-loader": "^3.2.1",
"react-dom": "^16.10.1",
"react-dom": "^17.0.2",
"rimraf": "^3.0.0",
"rollup": "^1.26.3",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-typescript2": "^0.25.2",
"ts-jest": "^24.1.0",
"typescript": "^3.7.2"
"ts-jest": "^29.4.6",
"typescript": "^4.9.0"
},
"dependencies": {
"throttle-debounce": "^2.1.0"
},
"husky": {
"hooks": {
"pre-commit": "yarn run ts-check && lint-staged"
}
},
"lint-staged": {
"*.{js,css,json,md}": [
"prettier --write",
"git add"
],
"*.js": [
"prettier --write",
"eslint --fix",
"git add"
"prettier --write"
],
"*.{ts,tsx}": [
"prettier --write",
"eslint --fix",
"git add"
"eslint --fix"
]
}
}
2 changes: 1 addition & 1 deletion rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ export default {
name: 'InfiniteScroll',
},
],
external: [...Object.keys(pkg.peerDependencies || {})],
external: [...Object.keys(pkg.peerDependencies || {}), 'react/jsx-runtime'],
plugins: [resolve(), typescript({ useTsconfigDeclarationDir: true })],
};
1 change: 0 additions & 1 deletion src/__tests__/bottom.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import InfiniteScroll from '../index';

Expand Down
1 change: 0 additions & 1 deletion src/__tests__/hasChildren.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import InfiniteScroll from '../index';

Expand Down
16 changes: 9 additions & 7 deletions src/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import InfiniteScroll from '../index';

Expand Down Expand Up @@ -57,6 +56,7 @@ describe('React Infinite Scroll Component', () => {

it('calls scroll handler if provided, when user scrolls', () => {
jest.useFakeTimers();
const setTimeoutSpy = jest.spyOn(global, 'setTimeout');
const onScrollMock = jest.fn();

const { container } = render(
Expand All @@ -79,21 +79,23 @@ describe('React Infinite Scroll Component', () => {

node.dispatchEvent(scrollEvent);
jest.runOnlyPendingTimers();
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeoutSpy).toHaveBeenCalled();
expect(onScrollMock).toHaveBeenCalled();
setTimeoutSpy.mockRestore();
});

describe('When missing the dataLength prop', () => {
it('throws an error', () => {
console.error = jest.fn();
const consoleSpy = jest
.spyOn(console, 'error')
.mockImplementation(() => {});
const props = { loader: 'Loading...', hasMore: false, next: () => {} };

// @ts-ignore
expect(() => render(<InfiniteScroll {...props} />)).toThrow(Error);
// @ts-ignore
expect(console.error.mock.calls[0][0]).toContain(
'"dataLength" is missing'
expect(() => render(<InfiniteScroll {...props} />)).toThrow(
'mandatory prop "dataLength" is missing'
);
consoleSpy.mockRestore();
});
});

Expand Down
1 change: 0 additions & 1 deletion src/__tests__/inverse.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import InfiniteScroll from '../index';

Expand Down
24 changes: 23 additions & 1 deletion src/__tests__/pullDown.test.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import InfiniteScroll from '../index';

describe('pull down to refresh', () => {
const originalGetBoundingClientRect = Element.prototype.getBoundingClientRect;

beforeAll(() => {
// ensure RAF exists
// @ts-ignore
global.requestAnimationFrame = (cb: any) => cb();
// Mock getBoundingClientRect to return a height for maxPullDownDistance
Element.prototype.getBoundingClientRect = jest.fn(() => ({
height: 100,
width: 0,
top: 0,
left: 0,
bottom: 0,
right: 0,
x: 0,
y: 0,
toJSON: () => ({}),
}));
});

afterAll(() => {
Element.prototype.getBoundingClientRect = originalGetBoundingClientRect;
});

afterEach(() => cleanup());

it('calls refreshFunction after pulling past threshold', () => {
Expand All @@ -22,6 +40,7 @@ describe('pull down to refresh', () => {
pullDownToRefresh
pullDownToRefreshThreshold={50}
refreshFunction={refresh}
pullDownToRefreshContent={<div style={{ height: 100 }}>Pull</div>}
>
<div />
</InfiniteScroll>
Expand All @@ -39,6 +58,9 @@ describe('pull down to refresh', () => {
Object.defineProperty(move, 'pageY', { value: 60 });
node.dispatchEvent(move);

// verify transform is applied during drag
expect(node.style.transform).toBe('translate3d(0px, 60px, 0px)');

const up = new MouseEvent('mouseup', { bubbles: true } as any);
node.dispatchEvent(up);

Expand Down
1 change: 0 additions & 1 deletion src/__tests__/scrollableTarget.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import InfiniteScroll from '../index';

Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/threshold.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@
});

it('warns and returns default for invalid string', () => {
const t = parseThreshold('foo' as any);

Check warning on line 30 in src/__tests__/threshold.test.ts

View workflow job for this annotation

GitHub Actions / test (18.x)

Unsafe argument of type `any` assigned to a parameter of type `string | number`

Check warning on line 30 in src/__tests__/threshold.test.ts

View workflow job for this annotation

GitHub Actions / test (22.x)

Unsafe argument of type `any` assigned to a parameter of type `string | number`

Check warning on line 30 in src/__tests__/threshold.test.ts

View workflow job for this annotation

GitHub Actions / test (20.x)

Unsafe argument of type `any` assigned to a parameter of type `string | number`
expect(t.unit).toBe(ThresholdUnits.Percent);
expect(t.value).toBe(0.8);
expect(warnSpy).toHaveBeenCalled();
});

it('warns and returns default for non-string/number', () => {
const t = parseThreshold((null as unknown) as any);
const t = parseThreshold(null as unknown as any);
expect(t.unit).toBe(ThresholdUnits.Percent);
expect(t.value).toBe(0.8);
expect(warnSpy).toHaveBeenCalled();
Expand Down
21 changes: 13 additions & 8 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Component, ReactNode, CSSProperties } from 'react';
import { Component, ReactNode, CSSProperties } from 'react';
import { throttle } from 'throttle-debounce';
import { ThresholdUnits, parseThreshold } from './utils/threshold';

Expand Down Expand Up @@ -52,7 +52,7 @@ export default class InfiniteScroll extends Component<Props, State> {

private throttledOnScrollListener: (e: MouseEvent) => void;
private _scrollableNode: HTMLElement | undefined | null;
private el: HTMLElement | undefined | Window & typeof globalThis;
private el: HTMLElement | undefined | (Window & typeof globalThis);
private _infScroll: HTMLDivElement | undefined;
private lastScrollTop = 0;
private actionTriggered = false;
Expand Down Expand Up @@ -81,8 +81,10 @@ export default class InfiniteScroll extends Component<Props, State> {
: this._scrollableNode || window;

if (this.el) {
this.el.addEventListener('scroll', this
.throttledOnScrollListener as EventListenerOrEventListenerObject);
this.el.addEventListener(
'scroll',
this.throttledOnScrollListener as EventListenerOrEventListenerObject
);
}

if (
Expand Down Expand Up @@ -124,8 +126,10 @@ export default class InfiniteScroll extends Component<Props, State> {

componentWillUnmount() {
if (this.el) {
this.el.removeEventListener('scroll', this
.throttledOnScrollListener as EventListenerOrEventListenerObject);
this.el.removeEventListener(
'scroll',
this.throttledOnScrollListener as EventListenerOrEventListenerObject
);

if (this.props.pullDownToRefresh) {
this.el.removeEventListener('touchstart', this.onStart);
Expand Down Expand Up @@ -223,8 +227,9 @@ export default class InfiniteScroll extends Component<Props, State> {

if (this._infScroll) {
this._infScroll.style.overflow = 'visible';
this._infScroll.style.transform = `translate3d(0px, ${this.currentY -
this.startY}px, 0px)`;
this._infScroll.style.transform = `translate3d(0px, ${
this.currentY - this.startY
}px, 0px)`;
}
};

Expand Down
1 change: 0 additions & 1 deletion src/stories/stories.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as React from 'react';
import { storiesOf } from '@storybook/react';

import WindowInf from './WindowInfiniteScrollComponent';
Expand Down
Loading
Loading