Socket
Socket
Sign inDemoInstall

react-intersection-observer

Package Overview
Dependencies
Maintainers
1
Versions
160
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-intersection-observer - npm Package Compare versions

Comparing version 9.1.0 to 9.2.0

react-intersection-observer.modern.js

2

InView.d.ts

@@ -64,3 +64,3 @@ import * as React from 'react';

unobserve(): void;
handleNode: (node?: Element | null | undefined) => void;
handleNode: (node?: Element | null) => void;
handleChange: (inView: boolean, entry: IntersectionObserverEntry) => void;

@@ -67,0 +67,0 @@ render(): React.ReactNode;

{
"name": "react-intersection-observer",
"version": "9.1.0",
"version": "9.2.0",
"description": "Monitor if a component is inside the viewport, using IntersectionObserver API",

@@ -5,0 +5,0 @@ "source": "src/index.tsx",

@@ -394,3 +394,3 @@ var React = require('react');

return /*#__PURE__*/React__namespace.createElement(as || 'div', _extends({
return React__namespace.createElement(as || 'div', _extends({
ref: this.handleNode

@@ -397,0 +397,0 @@ }, props), children);

@@ -374,3 +374,3 @@ import * as React from 'react';

return /*#__PURE__*/React.createElement(as || 'div', _extends({
return React.createElement(as || 'div', _extends({
ref: this.handleNode

@@ -377,0 +377,0 @@ }, props), children);

@@ -397,3 +397,3 @@ (function (global, factory) {

return /*#__PURE__*/React__namespace.createElement(as || 'div', _extends({
return React__namespace.createElement(as || 'div', _extends({
ref: this.handleNode

@@ -400,0 +400,0 @@ }, props), children);

@@ -27,3 +27,3 @@ # react-intersection-observer

- 🧪 **Ready to test** - Mocks the Intersection Observer for easy testing with
[Jest](https://jestjs.io/)
[Jest](https://jestjs.io/) or [Vitest](https://vitest.dev/)
- 🌳 **Tree-shakeable** - Only include the parts you use

@@ -92,3 +92,3 @@ - 💥 **Tiny bundle** - Around **~1.15kB** for `useInView` and **~1.6kB** for

`inView` prop, children also receive a `ref` that should be set on the
containing DOM element. This is the element that the IntersectionObserver will
containing DOM element. This is the element that the Intersection Observer will
monitor.

@@ -152,14 +152,14 @@

| Name | Type | Default | Required | Description |
| ---------------------- | ------------------------- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **root** | `Element` | document | false | The IntersectionObserver interface's read-only root property identifies the Element or Document whose bounds are treated as the bounding box of the viewport for the element which is the observer's target. If the root is `null`, then the bounds of the actual document viewport are used. |
| **rootMargin** | `string` | '0px' | false | Margin around the root. Can have values similar to the CSS margin property, e.g. "10px 20px 30px 40px" (top, right, bottom, left). |
| **threshold** | `number` or `number[]` | 0 | false | Number between `0` and `1` indicating the percentage that should be visible before triggering. Can also be an array of numbers, to create multiple trigger points. |
| **onChange** | `(inView, entry) => void` | undefined | false | Call this function whenever the in view state changes. It will receive the `inView` boolean, alongside the current `IntersectionObserverEntry`. |
| **trackVisibility** 🧪 | `boolean` | false | false | A boolean indicating whether this IntersectionObserver will track visibility changes on the target. |
| **delay** 🧪 | `number` | undefined | false | A number indicating the minimum delay in milliseconds between notifications from this observer for a given target. This must be set to at least `100` if `trackVisibility` is `true`. |
| **skip** | `boolean` | false | false | Skip creating the IntersectionObserver. You can use this to enable and disable the observer as needed. If `skip` is set while `inView`, the current state will still be kept. |
| **triggerOnce** | `boolean` | false | false | Only trigger the observer once. |
| **initialInView** | `boolean` | false | false | Set the initial value of the `inView` boolean. This can be used if you expect the element to be in the viewport to start with, and you want to trigger something when it leaves. |
| **fallbackInView** | `boolean` | undefined | false | If the `IntersectionObserver` API isn't available in the client, the default behavior is to throw an Error. You can set a specific fallback behavior, and the `inView` value will be set to this instead of failing. To set a global default, you can set it with the `defaultFallbackInView()` |
| Name | Type | Default | Description |
| ---------------------- | ------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **root** | `Element` | `document` | The Intersection Observer interface's read-only root property identifies the Element or Document whose bounds are treated as the bounding box of the viewport for the element which is the observer's target. If the root is `null`, then the bounds of the actual document viewport are used. |
| **rootMargin** | `string` | `'0px'` | Margin around the root. Can have values similar to the CSS margin property, e.g. `"10px 20px 30px 40px"` (top, right, bottom, left). |
| **threshold** | `number` or `number[]` | `0` | Number between `0` and `1` indicating the percentage that should be visible before triggering. Can also be an array of numbers, to create multiple trigger points. |
| **onChange** | `(inView, entry) => void` | `undefined` | Call this function whenever the in view state changes. It will receive the `inView` boolean, alongside the current `IntersectionObserverEntry`. |
| **trackVisibility** 🧪 | `boolean` | `false` | A boolean indicating whether this Intersection Observer will track visibility changes on the target. |
| **delay** 🧪 | `number` | `undefined` | A number indicating the minimum delay in milliseconds between notifications from this observer for a given target. This must be set to at least `100` if `trackVisibility` is `true`. |
| **skip** | `boolean` | `false` | Skip creating the IntersectionObserver. You can use this to enable and disable the observer as needed. If `skip` is set while `inView`, the current state will still be kept. |
| **triggerOnce** | `boolean` | `false` | Only trigger the observer once. |
| **initialInView** | `boolean` | `false` | Set the initial value of the `inView` boolean. This can be used if you expect the element to be in the viewport to start with, and you want to trigger something when it leaves. |
| **fallbackInView** | `boolean` | `undefined` | If the `IntersectionObserver` API isn't available in the client, the default behavior is to throw an Error. You can set a specific fallback behavior, and the `inView` value will be set to this instead of failing. To set a global default, you can set it with the `defaultFallbackInView()` |

@@ -170,8 +170,8 @@ ### InView Props

| Name | Type | Default | Required | Description |
| ------------ | ---------------------------------------------------- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **as** | `string` | 'div' | false | Render the wrapping element as this element. Defaults to `div`. |
| **children** | `({ref, inView, entry}) => ReactNode` or `ReactNode` | undefined | true | Children expects a function that receives an object containing the `inView` boolean and a `ref` that should be assigned to the element root. Alternatively pass a plain child, to have the `<InView />` deal with the wrapping element. You will also get the `IntersectionObserverEntry` as `entry, giving you more details. |
| Name | Type | Default | Description |
| ------------ | ---------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **as** | `string` | `'div'` | Render the wrapping element as this element. Defaults to `div`. |
| **children** | `({ref, inView, entry}) => ReactNode` or `ReactNode` | `undefined` | Children expects a function that receives an object containing the `inView` boolean and a `ref` that should be assigned to the element root. Alternatively pass a plain child, to have the `<InView />` deal with the wrapping element. You will also get the `IntersectionObserverEntry` as `entry, giving you more details. |
### IntersectionObserver v2 🧪
### Intersection Observer v2 🧪

@@ -250,3 +250,3 @@ The new

- [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#The_intersection_root_and_root_margin)
- [w3c/IntersectionObserver: IntersectionObserver rootMargin ignored within iframe](https://github.com/w3c/IntersectionObserver/issues/283#issuecomment-507397917)
- [w3c/IntersectionObserver: rootMargin ignored within iframe](https://github.com/w3c/IntersectionObserver/issues/283#issuecomment-507397917)
- [w3c/IntersectionObserver: Cannot track intersection with an iframe's viewport](https://github.com/w3c/IntersectionObserver/issues/372)

@@ -258,59 +258,86 @@ - [w3c/Support iframe viewport tracking](https://github.com/w3c/IntersectionObserver/pull/465)

In order to write meaningful tests, the `IntersectionObserver` needs to be
mocked. If you are writing your tests in Jest, you can use the included
`test-utils.js`. It mocks the `IntersectionObserver`, and includes a few methods
mocked. You can use the included `react-intersection-observer/test-utils` to
help with this. It mocks the `IntersectionObserver`, and includes a few methods
to assist with faking the `inView` state. When setting the `isIntersecting`
value you can pass either a `boolean` value or a threshold between `0` and `1`.
value you can pass either a `boolean` value or a threshold between 0 and 1.It
wil emulate the real IntersectionObserver, allowing you to validate that your
components are behaving as expected.
### `test-utils.js`
| Method | Description |
| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mockAllIsIntersecting(isIntersecting)` | Set `isIntersecting` on all current Intersection Observer instances. The value of `isIntersecting` should be either a `boolean` or a threshold between 0 and 1. |
| `mockIsIntersecting(element, isIntersecting)` | Set `isIntersecting` for the Intersection Observer of a specific `element`. The value of `isIntersecting` should be either a `boolean` or a threshold between 0 and 1. |
| `intersectionMockInstance(element)` | Call the `intersectionMockInstance` method with an element, to get the (mocked) `IntersectionObserver` instance. You can use this to spy on the `observe` and`unobserve` methods. |
| `setupIntersectionMocking(mockFn)` | Mock the `IntersectionObserver`, so we can interact with them in tests - Should be called in `beforeEach`. (**Done automatically in Jest environment**) |
| `resetIntersectionMocking()` | Reset the mocks on `IntersectionObserver` - Should be called in `afterEach`. (**Done automatically in Jest environment**) |
You can use these test utilities as imports in individual files OR you can
globally mock Intersection Observer for all Jest tests. If you use a library or
an application with a lot of Intersection Observer usage, you may wish to
globally mock it; however, the official recommendation is to be purposeful about
your mocking and do so on a per-usage basis.
### Testing Libraries
#### Indvidual Methods
This library comes with built-in support for writing tests in both
[Jest](https://jestjs.io/) and [Vitest](https://vitest.dev/)
Import these from `react-intersection-observer/test-utils`.
#### Jest
**`mockAllIsIntersecting(isIntersecting:boolean | number)`**
Set `isIntersecting` on all current IntersectionObserver instances.
Testing with Jest should work out of the box. Just import the
`react-intersection-observer/test-utils` in your test files, and you can use the
mocking methods.
**`mockIsIntersecting(element:Element, isIntersecting:boolean | number)`**
Set `isIntersecting` for the IntersectionObserver of a specific element.
#### Vitest
**`intersectionMockInstance(element:Element): IntersectionObserver`**
Call the `intersectionMockInstance` method with an element, to get the (mocked)
`IntersectionObserver` instance. You can use this to spy on the `observe` and
`unobserve` methods.
If you're running Vitest with [globals](https://vitest.dev/config/#globals),
then it'll automatically mock the IntersectionObserver, just like running with
Jest. Otherwise, you'll need to manually setup/reset the mocking in either the
individual tests, or a [setup file](https://vitest.dev/config/#setupfiles).
#### Global Intersection Observer Behavior
```js
import { vi, beforeEach, afterEach } from 'vitest';
import {
setupIntersectionMocking,
resetIntersectionMocking,
} from 'react-intersection-observer/test-utils';
##### Use Fallback
beforeEach(() => {
setupIntersectionMocking(jest.fn);
});
afterEach(() => {
resetIntersectionMocking();
});
```
> ⚠️ You only need to do this if the test environment does not support
> `beforeEach` globally, alongside either `jest.fn` or `vi.fn`.
#### Other Testing Libraries
See the instructions for [Vitest](#vitest). You should be able to use a similar
setup/reset code, adapted to the testing library you are using. Failing that,
copy the code from [test-utils.ts][test-utils-url], and make your own version.
### Fallback Behavior
You can create a
[Jest setup file](https://jestjs.io/docs/configuration#setupfiles-array) that
leverages the
[Jest setup file](https://jestjs.io/docs/configuration#setupfilesafterenv-array)
that leverages the
[unsupported fallback](https://github.com/thebuilder/react-intersection-observer#unsupported-fallback)
option. In this case, you only mock the IntersectionObserver in test files were
you actively import `react-intersection-observer/test-utils`:
option. In this case, you can override the `IntersectionObserver` in test files
were you actively import `react-intersection-observer/test-utils`.
**test-setup.js**
```js
import { defaultFallbackInView } from 'react-intersection-observer';
defaultFallbackInView(true); // or 'false' - whichever consistent behavior makes the most sense for your use case.
defaultFallbackInView(true); // or `false` - whichever consistent behavior makes the most sense for your use case.
```
##### Mock Everywhere
Alternatively, you can mock the Intersection Observer in all tests with a global
setup file. Add `react-intersection-observer/test-utils` to
[setupFilesAfterEnv](https://jestjs.io/docs/configuration#setupfilesafterenv-array)
in the Jest config, or [setupFiles](https://vitest.dev/config/#setupfiles) in
Vitest.
In your Jest config, add `'react-intersection-observer/test-utils'` to the array
value for the
[`setupFilesAfterEnv`](https://jestjs.io/docs/configuration#setupfilesafterenv-array)
option.
```js
module.exports = {
// other config lines
setupFilesAfterEnv: ['react-intersection-observer/test-utils'],
// other config lines
};

@@ -325,7 +352,15 @@ ```

import { useInView } from 'react-intersection-observer';
import { mockAllIsIntersecting } from 'react-intersection-observer/test-utils';
import {
mockAllIsIntersecting,
mockIsIntersecting,
intersectionMockInstance,
} from 'react-intersection-observer/test-utils';
const HookComponent = ({ options }) => {
const [ref, inView] = useInView(options);
return <div ref={ref}>{inView.toString()}</div>;
const { ref, inView } = useInView(options);
return (
<div ref={ref} id="wrapper">
{inView.toString()}
</div>
);
};

@@ -351,2 +386,20 @@

});
test('should mock intersecing on specific hook', () => {
render(<HookComponent />);
const wrapper = screen.getByTestId('wrapper');
// Set the intersection state on the wrapper.
mockIsIntersecting(wrapper, 0.5);
screen.getByText('true');
});
test('should create a hook and call observe', () => {
const { getByTestId } = render(<HookComponent />);
const wrapper = getByTestId('wrapper');
// Access the `IntersectionObserver` instance for the wrapper Element.
const instance = intersectionMockInstance(wrapper);
expect(instance.observe).toHaveBeenCalledWith(wrapper);
});
```

@@ -358,3 +411,3 @@

is the API used to determine if an element is inside the viewport or not.
[Browser support is really good](http://caniuse.com/#feat=intersectionobserver) -
[Browser support is excellent](http://caniuse.com/#feat=intersectionobserver) -
With

@@ -449,7 +502,7 @@ [Safari adding support in 12.1](https://webkit.org/blog/8718/new-webkit-features-in-safari-12-1/),

| Name | Type | Required | Description |
| ------------ | -------------------------- | -------- | --------------------------------------------------------- |
| **element** | `Element` | true | DOM element to observe |
| **callback** | `ObserverInstanceCallback` | true | The callback function that IntersectionObserver will call |
| **options** | `IntersectionObserverInit` | false | The options for the IntersectionObserver |
| Name | Type | Required | Description |
| ------------ | -------------------------- | -------- | ---------------------------------------------------------- |
| **element** | `Element` | true | DOM element to observe |
| **callback** | `ObserverInstanceCallback` | true | The callback function that Intersection Observer will call |
| **options** | `IntersectionObserverInit` | false | The options for the Intersection Observer |

@@ -478,1 +531,3 @@ The `observe` method returns an `unobserve` function, that you must call in

https://github.com/thebuilder/react-intersection-observer/actions?query=workflow%3ATest
[test-utils-url]:
https://github.com/thebuilder/react-intersection-observer/blob/feat/vitest/src/test-utils.ts
/**
* Create a custom IntersectionObserver mock, allowing us to intercept the `observe` and `unobserve` calls.
* We keep track of the elements being observed, so when `mockAllIsIntersecting` is triggered it will
* know which elements to trigger the event on.
* @param mockFn The mock function to use. Defaults to `jest.fn`.
*/
export declare function setupIntersectionMocking(mockFn: typeof jest.fn): void;
/**
* Rest the IntersectionObserver mock to its initial state, and clear all the elements being observed.
*/
export declare function resetIntersectionMocking(): void;
/**
* Set the `isIntersecting` on all current IntersectionObserver instances

@@ -3,0 +14,0 @@ * @param isIntersecting {boolean | number}

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.intersectionMockInstance = exports.mockIsIntersecting = exports.mockAllIsIntersecting = void 0;
exports.intersectionMockInstance = exports.mockIsIntersecting = exports.mockAllIsIntersecting = exports.resetIntersectionMocking = exports.setupIntersectionMocking = void 0;
const test_utils_1 = require("react-dom/test-utils");
let isMocking = false;
const observers = new Map();
beforeEach(() => {
/**
* Create a custom IntersectionObserver mock, allowing us to intercept the observe and unobserve calls.
* We keep track of the elements being observed, so when `mockAllIsIntersecting` is triggered it will
* know which elements to trigger the event on.
*/
global.IntersectionObserver = jest.fn((cb, options = {}) => {
// If we are running in a valid testing environment, we can mock the IntersectionObserver.
if (typeof beforeEach !== 'undefined' && typeof afterEach !== 'undefined') {
beforeEach(() => {
// Use the exposed mock function. Currently, only supports Jest (`jest.fn`) and Vitest with globals (`vi.fn`).
if (typeof jest !== 'undefined')
setupIntersectionMocking(jest.fn);
else if (typeof vi !== 'undefined')
setupIntersectionMocking(vi.fn);
});
afterEach(() => {
resetIntersectionMocking();
});
}
function warnOnMissingSetup() {
if (isMocking)
return;
console.error('React Intersection Observer was not configured to handle mocking.\n' +
'Outside Jest, you might need to manually configure it by calling setupIntersectionMocking() and resetIntersectionMocking() in your test setup file.', +`\n// test-setup.js
import { resetIntersectionMocking, setupIntersectionMocking } from 'react-intersection-observer/test-utils';
beforeEach(() => {
setupIntersectionMocking(vi.fn);
});
afterEach(() => {
resetIntersectionMocking();
});`);
}
/**
* Create a custom IntersectionObserver mock, allowing us to intercept the `observe` and `unobserve` calls.
* We keep track of the elements being observed, so when `mockAllIsIntersecting` is triggered it will
* know which elements to trigger the event on.
* @param mockFn The mock function to use. Defaults to `jest.fn`.
*/
function setupIntersectionMocking(mockFn) {
global.IntersectionObserver = mockFn((cb, options = {}) => {
var _a, _b, _c;

@@ -25,12 +55,12 @@ const item = {

rootMargin: (_c = options.rootMargin) !== null && _c !== void 0 ? _c : '',
observe: jest.fn((element) => {
observe: mockFn((element) => {
item.elements.add(element);
}),
unobserve: jest.fn((element) => {
unobserve: mockFn((element) => {
item.elements.delete(element);
}),
disconnect: jest.fn(() => {
disconnect: mockFn(() => {
observers.delete(instance);
}),
takeRecords: jest.fn(),
takeRecords: mockFn(),
};

@@ -40,4 +70,9 @@ observers.set(instance, item);

});
});
afterEach(() => {
isMocking = true;
}
exports.setupIntersectionMocking = setupIntersectionMocking;
/**
* Rest the IntersectionObserver mock to its initial state, and clear all the elements being observed.
*/
function resetIntersectionMocking() {
// @ts-ignore

@@ -47,3 +82,4 @@ if (global.IntersectionObserver)

observers.clear();
});
}
exports.resetIntersectionMocking = resetIntersectionMocking;
function triggerIntersection(elements, trigger, observer, item) {

@@ -102,2 +138,3 @@ const entries = [];

function mockAllIsIntersecting(isIntersecting) {
warnOnMissingSetup();
for (let [observer, item] of observers) {

@@ -115,2 +152,3 @@ triggerIntersection(Array.from(item.elements), isIntersecting, observer, item);

function mockIsIntersecting(element, isIntersecting) {
warnOnMissingSetup();
const observer = intersectionMockInstance(element);

@@ -134,2 +172,3 @@ if (!observer) {

function intersectionMockInstance(element) {
warnOnMissingSetup();
for (let [observer, item] of observers) {

@@ -136,0 +175,0 @@ if (item.elements.has(element)) {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc