react-sway 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.cjs +39 -3
- package/dist/index.js +39 -3
- package/package.json +12 -9
- package/src/ReactSway.tsx +76 -3
- package/src/__tests__/ReactSway.test.tsx +66 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ It works by duplicating your content to create a seamless loop and uses CSS tran
|
|
|
15
15
|
* **User Friendly Interactions:**
|
|
16
16
|
* Click and drag to scroll.
|
|
17
17
|
* Swipe on touch devices.
|
|
18
|
-
* Mouse wheel support with velocity capping.
|
|
18
|
+
* Mouse wheel support with delta-mode normalization and velocity capping.
|
|
19
19
|
* Keyboard controls: Spacebar to pause/resume, ArrowUp/ArrowDown to scroll, Home/End to jump.
|
|
20
20
|
* **Responsive:** Adjusts to window resizing with debounced recalculation.
|
|
21
21
|
* **Lazy Visibility Detection:** Add a `content-item` class to your child elements, and `react-sway` automatically uses an IntersectionObserver to add a `.visible` class when they enter the viewport. Useful for triggering CSS animations or deferred rendering. Configurable via `lazy`, `lazyRootMargin`, and `lazyThreshold` props.
|
package/dist/index.cjs
CHANGED
|
@@ -39,7 +39,37 @@ var MAX_VELOCITY = 150;
|
|
|
39
39
|
var MS_PER_FRAME_60FPS = 16.667;
|
|
40
40
|
var REDUCED_MOTION_SPEED_FACTOR = 0.25;
|
|
41
41
|
var RESIZE_DEBOUNCE_MS = 150;
|
|
42
|
-
var
|
|
42
|
+
var WHEEL_LINE_HEIGHT_FALLBACK_PX = 16;
|
|
43
|
+
var WHEEL_MODE_LINE = 1;
|
|
44
|
+
var WHEEL_MODE_PAGE = 2;
|
|
45
|
+
var WHEEL_PIXEL_MIN_DELTA_PX = 48;
|
|
46
|
+
var WHEEL_VELOCITY_MULTIPLIER = 0.14;
|
|
47
|
+
var WHEEL_IMMEDIATE_DELTA_FACTOR = 0.14;
|
|
48
|
+
var WHEEL_LEGACY_NOTCH_DELTA = 120;
|
|
49
|
+
function getLegacyWheelPixelDeltaY(event) {
|
|
50
|
+
const { wheelDelta, wheelDeltaY } = event;
|
|
51
|
+
const legacyWheelDelta = typeof wheelDeltaY === "number" ? wheelDeltaY : wheelDelta;
|
|
52
|
+
if (typeof legacyWheelDelta !== "number" || !Number.isFinite(legacyWheelDelta)) {
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
return -(legacyWheelDelta / WHEEL_LEGACY_NOTCH_DELTA) * WHEEL_PIXEL_MIN_DELTA_PX;
|
|
56
|
+
}
|
|
57
|
+
function normalizeWheelDeltaY(event, container) {
|
|
58
|
+
if (event.deltaMode === WHEEL_MODE_LINE) {
|
|
59
|
+
const computedLineHeight = Number.parseFloat(window.getComputedStyle(container).lineHeight);
|
|
60
|
+
const lineHeight = Number.isFinite(computedLineHeight) ? computedLineHeight : WHEEL_LINE_HEIGHT_FALLBACK_PX;
|
|
61
|
+
return event.deltaY * lineHeight;
|
|
62
|
+
}
|
|
63
|
+
if (event.deltaMode === WHEEL_MODE_PAGE) {
|
|
64
|
+
const pageHeight = Math.max(container.clientHeight, window.innerHeight, 1);
|
|
65
|
+
return event.deltaY * pageHeight;
|
|
66
|
+
}
|
|
67
|
+
const legacyWheelPixelDeltaY = getLegacyWheelPixelDeltaY(event);
|
|
68
|
+
if (Math.abs(event.deltaY) < WHEEL_PIXEL_MIN_DELTA_PX && Math.abs(legacyWheelPixelDeltaY) >= WHEEL_PIXEL_MIN_DELTA_PX) {
|
|
69
|
+
return legacyWheelPixelDeltaY;
|
|
70
|
+
}
|
|
71
|
+
return event.deltaY;
|
|
72
|
+
}
|
|
43
73
|
function ReactSway({
|
|
44
74
|
autoScroll = true,
|
|
45
75
|
children,
|
|
@@ -292,12 +322,18 @@ function ReactSway({
|
|
|
292
322
|
}, [draggable, pauseAutoScroll]);
|
|
293
323
|
const handleWheel = (0, import_react.useCallback)((e) => {
|
|
294
324
|
if (!wheelEnabled) return;
|
|
325
|
+
const currentContainer = e.currentTarget instanceof HTMLElement ? e.currentTarget : containerRef.current;
|
|
326
|
+
if (!currentContainer) return;
|
|
295
327
|
e.preventDefault();
|
|
296
|
-
|
|
328
|
+
const normalizedDeltaY = normalizeWheelDeltaY(e, currentContainer);
|
|
329
|
+
const wheelDelta = -normalizedDeltaY;
|
|
330
|
+
const nextPosition = wrapPosition(positionRef.current + wheelDelta * WHEEL_IMMEDIATE_DELTA_FACTOR);
|
|
331
|
+
commitPosition(nextPosition);
|
|
332
|
+
velocityRef.current += wheelDelta * WHEEL_VELOCITY_MULTIPLIER;
|
|
297
333
|
velocityRef.current = Math.max(-MAX_VELOCITY, Math.min(MAX_VELOCITY, velocityRef.current));
|
|
298
334
|
pauseAutoScroll();
|
|
299
335
|
scheduleAutoScrollResume();
|
|
300
|
-
}, [pauseAutoScroll, scheduleAutoScrollResume, wheelEnabled]);
|
|
336
|
+
}, [commitPosition, pauseAutoScroll, scheduleAutoScrollResume, wheelEnabled, wrapPosition]);
|
|
301
337
|
(0, import_react.useEffect)(() => {
|
|
302
338
|
const currentContainer = containerRef.current;
|
|
303
339
|
if (!currentContainer) return;
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,37 @@ var MAX_VELOCITY = 150;
|
|
|
13
13
|
var MS_PER_FRAME_60FPS = 16.667;
|
|
14
14
|
var REDUCED_MOTION_SPEED_FACTOR = 0.25;
|
|
15
15
|
var RESIZE_DEBOUNCE_MS = 150;
|
|
16
|
-
var
|
|
16
|
+
var WHEEL_LINE_HEIGHT_FALLBACK_PX = 16;
|
|
17
|
+
var WHEEL_MODE_LINE = 1;
|
|
18
|
+
var WHEEL_MODE_PAGE = 2;
|
|
19
|
+
var WHEEL_PIXEL_MIN_DELTA_PX = 48;
|
|
20
|
+
var WHEEL_VELOCITY_MULTIPLIER = 0.14;
|
|
21
|
+
var WHEEL_IMMEDIATE_DELTA_FACTOR = 0.14;
|
|
22
|
+
var WHEEL_LEGACY_NOTCH_DELTA = 120;
|
|
23
|
+
function getLegacyWheelPixelDeltaY(event) {
|
|
24
|
+
const { wheelDelta, wheelDeltaY } = event;
|
|
25
|
+
const legacyWheelDelta = typeof wheelDeltaY === "number" ? wheelDeltaY : wheelDelta;
|
|
26
|
+
if (typeof legacyWheelDelta !== "number" || !Number.isFinite(legacyWheelDelta)) {
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
return -(legacyWheelDelta / WHEEL_LEGACY_NOTCH_DELTA) * WHEEL_PIXEL_MIN_DELTA_PX;
|
|
30
|
+
}
|
|
31
|
+
function normalizeWheelDeltaY(event, container) {
|
|
32
|
+
if (event.deltaMode === WHEEL_MODE_LINE) {
|
|
33
|
+
const computedLineHeight = Number.parseFloat(window.getComputedStyle(container).lineHeight);
|
|
34
|
+
const lineHeight = Number.isFinite(computedLineHeight) ? computedLineHeight : WHEEL_LINE_HEIGHT_FALLBACK_PX;
|
|
35
|
+
return event.deltaY * lineHeight;
|
|
36
|
+
}
|
|
37
|
+
if (event.deltaMode === WHEEL_MODE_PAGE) {
|
|
38
|
+
const pageHeight = Math.max(container.clientHeight, window.innerHeight, 1);
|
|
39
|
+
return event.deltaY * pageHeight;
|
|
40
|
+
}
|
|
41
|
+
const legacyWheelPixelDeltaY = getLegacyWheelPixelDeltaY(event);
|
|
42
|
+
if (Math.abs(event.deltaY) < WHEEL_PIXEL_MIN_DELTA_PX && Math.abs(legacyWheelPixelDeltaY) >= WHEEL_PIXEL_MIN_DELTA_PX) {
|
|
43
|
+
return legacyWheelPixelDeltaY;
|
|
44
|
+
}
|
|
45
|
+
return event.deltaY;
|
|
46
|
+
}
|
|
17
47
|
function ReactSway({
|
|
18
48
|
autoScroll = true,
|
|
19
49
|
children,
|
|
@@ -266,12 +296,18 @@ function ReactSway({
|
|
|
266
296
|
}, [draggable, pauseAutoScroll]);
|
|
267
297
|
const handleWheel = useCallback((e) => {
|
|
268
298
|
if (!wheelEnabled) return;
|
|
299
|
+
const currentContainer = e.currentTarget instanceof HTMLElement ? e.currentTarget : containerRef.current;
|
|
300
|
+
if (!currentContainer) return;
|
|
269
301
|
e.preventDefault();
|
|
270
|
-
|
|
302
|
+
const normalizedDeltaY = normalizeWheelDeltaY(e, currentContainer);
|
|
303
|
+
const wheelDelta = -normalizedDeltaY;
|
|
304
|
+
const nextPosition = wrapPosition(positionRef.current + wheelDelta * WHEEL_IMMEDIATE_DELTA_FACTOR);
|
|
305
|
+
commitPosition(nextPosition);
|
|
306
|
+
velocityRef.current += wheelDelta * WHEEL_VELOCITY_MULTIPLIER;
|
|
271
307
|
velocityRef.current = Math.max(-MAX_VELOCITY, Math.min(MAX_VELOCITY, velocityRef.current));
|
|
272
308
|
pauseAutoScroll();
|
|
273
309
|
scheduleAutoScrollResume();
|
|
274
|
-
}, [pauseAutoScroll, scheduleAutoScrollResume, wheelEnabled]);
|
|
310
|
+
}, [commitPosition, pauseAutoScroll, scheduleAutoScrollResume, wheelEnabled, wrapPosition]);
|
|
275
311
|
useEffect(() => {
|
|
276
312
|
const currentContainer = containerRef.current;
|
|
277
313
|
if (!currentContainer) return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"author": {
|
|
3
|
-
"name": "Mehdy
|
|
3
|
+
"name": "Lafitte Mehdy",
|
|
4
4
|
"url": "https://github.com/lafittemehdy"
|
|
5
5
|
},
|
|
6
6
|
"bugs": {
|
|
@@ -8,19 +8,21 @@
|
|
|
8
8
|
},
|
|
9
9
|
"description": "A React component for smooth infinite scrolling, designed for creating engaging, continuous content streams with minimal configuration.",
|
|
10
10
|
"devDependencies": {
|
|
11
|
+
"@emnapi/core": "^1.10.0",
|
|
12
|
+
"@emnapi/runtime": "^1.10.0",
|
|
11
13
|
"@testing-library/jest-dom": "^6.9.1",
|
|
12
14
|
"@testing-library/react": "^16.3.2",
|
|
13
15
|
"@types/react": "^19.2.14",
|
|
14
16
|
"@types/react-dom": "^19.2.3",
|
|
15
17
|
"eslint": "^9.39.4",
|
|
16
18
|
"eslint-plugin-react": "^7.37.5",
|
|
17
|
-
"eslint-plugin-react-hooks": "^7.
|
|
18
|
-
"jsdom": "^
|
|
19
|
-
"react": "^19.2.
|
|
20
|
-
"react-dom": "^19.2.
|
|
19
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
20
|
+
"jsdom": "^29.1.1",
|
|
21
|
+
"react": "^19.2.6",
|
|
22
|
+
"react-dom": "^19.2.6",
|
|
21
23
|
"tsup": "^8.5.1",
|
|
22
|
-
"typescript": "^
|
|
23
|
-
"vitest": "^4.1.
|
|
24
|
+
"typescript": "^6.0.3",
|
|
25
|
+
"vitest": "^4.1.6"
|
|
24
26
|
},
|
|
25
27
|
"files": [
|
|
26
28
|
"dist",
|
|
@@ -49,7 +51,8 @@
|
|
|
49
51
|
"react-dom": ">=16.8.0"
|
|
50
52
|
},
|
|
51
53
|
"publishConfig": {
|
|
52
|
-
"access": "public"
|
|
54
|
+
"access": "public",
|
|
55
|
+
"provenance": true
|
|
53
56
|
},
|
|
54
57
|
"repository": {
|
|
55
58
|
"type": "git",
|
|
@@ -65,5 +68,5 @@
|
|
|
65
68
|
},
|
|
66
69
|
"type": "module",
|
|
67
70
|
"types": "dist/index.d.ts",
|
|
68
|
-
"version": "0.2.
|
|
71
|
+
"version": "0.2.2"
|
|
69
72
|
}
|
package/src/ReactSway.tsx
CHANGED
|
@@ -36,8 +36,74 @@ const REDUCED_MOTION_SPEED_FACTOR = 0.25;
|
|
|
36
36
|
/** Debounce delay in milliseconds for ResizeObserver callbacks. */
|
|
37
37
|
const RESIZE_DEBOUNCE_MS = 150;
|
|
38
38
|
|
|
39
|
+
/** Fallback pixel height for wheel events reported in line units. */
|
|
40
|
+
const WHEEL_LINE_HEIGHT_FALLBACK_PX = 16;
|
|
41
|
+
|
|
42
|
+
/** WheelEvent deltaMode value for line-based deltas. */
|
|
43
|
+
const WHEEL_MODE_LINE = 1;
|
|
44
|
+
|
|
45
|
+
/** WheelEvent deltaMode value for page-based deltas. */
|
|
46
|
+
const WHEEL_MODE_PAGE = 2;
|
|
47
|
+
|
|
48
|
+
/** Minimum pixel impulse for discrete wheel hardware reporting tiny pixel deltas. */
|
|
49
|
+
const WHEEL_PIXEL_MIN_DELTA_PX = 48;
|
|
50
|
+
|
|
39
51
|
/** Multiplier applied to wheel deltaY to convert to scroll velocity. */
|
|
40
|
-
const WHEEL_VELOCITY_MULTIPLIER = 0.
|
|
52
|
+
const WHEEL_VELOCITY_MULTIPLIER = 0.14;
|
|
53
|
+
|
|
54
|
+
/** Small immediate wheel movement used to keep input responsive without jumping. */
|
|
55
|
+
const WHEEL_IMMEDIATE_DELTA_FACTOR = 0.14;
|
|
56
|
+
|
|
57
|
+
/** Legacy wheelDelta magnitude that usually represents one physical wheel notch. */
|
|
58
|
+
const WHEEL_LEGACY_NOTCH_DELTA = 120;
|
|
59
|
+
|
|
60
|
+
interface WheelEventWithLegacyDelta extends globalThis.WheelEvent {
|
|
61
|
+
wheelDelta?: number;
|
|
62
|
+
wheelDeltaY?: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getLegacyWheelPixelDeltaY(event: globalThis.WheelEvent) {
|
|
66
|
+
const { wheelDelta, wheelDeltaY } = event as WheelEventWithLegacyDelta;
|
|
67
|
+
const legacyWheelDelta = typeof wheelDeltaY === 'number' ? wheelDeltaY : wheelDelta;
|
|
68
|
+
|
|
69
|
+
if (typeof legacyWheelDelta !== 'number' || !Number.isFinite(legacyWheelDelta)) {
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return -(legacyWheelDelta / WHEEL_LEGACY_NOTCH_DELTA) * WHEEL_PIXEL_MIN_DELTA_PX;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Converts wheel deltas to pixels so mouse wheels, touchpads, and page-wheel
|
|
78
|
+
* devices feed the same Sway velocity system.
|
|
79
|
+
*/
|
|
80
|
+
function normalizeWheelDeltaY(event: globalThis.WheelEvent, container: HTMLElement) {
|
|
81
|
+
if (event.deltaMode === WHEEL_MODE_LINE) {
|
|
82
|
+
const computedLineHeight = Number.parseFloat(window.getComputedStyle(container).lineHeight);
|
|
83
|
+
const lineHeight = Number.isFinite(computedLineHeight)
|
|
84
|
+
? computedLineHeight
|
|
85
|
+
: WHEEL_LINE_HEIGHT_FALLBACK_PX;
|
|
86
|
+
|
|
87
|
+
return event.deltaY * lineHeight;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (event.deltaMode === WHEEL_MODE_PAGE) {
|
|
91
|
+
const pageHeight = Math.max(container.clientHeight, window.innerHeight, 1);
|
|
92
|
+
|
|
93
|
+
return event.deltaY * pageHeight;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const legacyWheelPixelDeltaY = getLegacyWheelPixelDeltaY(event);
|
|
97
|
+
|
|
98
|
+
if (
|
|
99
|
+
Math.abs(event.deltaY) < WHEEL_PIXEL_MIN_DELTA_PX &&
|
|
100
|
+
Math.abs(legacyWheelPixelDeltaY) >= WHEEL_PIXEL_MIN_DELTA_PX
|
|
101
|
+
) {
|
|
102
|
+
return legacyWheelPixelDeltaY;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return event.deltaY;
|
|
106
|
+
}
|
|
41
107
|
|
|
42
108
|
/**
|
|
43
109
|
* Props for the ReactSway infinite scrolling component.
|
|
@@ -382,12 +448,19 @@ function ReactSway({
|
|
|
382
448
|
|
|
383
449
|
const handleWheel = useCallback((e: globalThis.WheelEvent) => {
|
|
384
450
|
if (!wheelEnabled) return;
|
|
451
|
+
const currentContainer = e.currentTarget instanceof HTMLElement ? e.currentTarget : containerRef.current;
|
|
452
|
+
if (!currentContainer) return;
|
|
453
|
+
|
|
385
454
|
e.preventDefault();
|
|
386
|
-
|
|
455
|
+
const normalizedDeltaY = normalizeWheelDeltaY(e, currentContainer);
|
|
456
|
+
const wheelDelta = -normalizedDeltaY;
|
|
457
|
+
const nextPosition = wrapPosition(positionRef.current + wheelDelta * WHEEL_IMMEDIATE_DELTA_FACTOR);
|
|
458
|
+
commitPosition(nextPosition);
|
|
459
|
+
velocityRef.current += wheelDelta * WHEEL_VELOCITY_MULTIPLIER;
|
|
387
460
|
velocityRef.current = Math.max(-MAX_VELOCITY, Math.min(MAX_VELOCITY, velocityRef.current));
|
|
388
461
|
pauseAutoScroll();
|
|
389
462
|
scheduleAutoScrollResume();
|
|
390
|
-
}, [pauseAutoScroll, scheduleAutoScrollResume, wheelEnabled]);
|
|
463
|
+
}, [commitPosition, pauseAutoScroll, scheduleAutoScrollResume, wheelEnabled, wrapPosition]);
|
|
391
464
|
|
|
392
465
|
// Event listener registration
|
|
393
466
|
useEffect(() => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Behavioral and regression tests for ReactSway.
|
|
3
3
|
*/
|
|
4
|
-
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
|
|
4
|
+
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
5
5
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
6
6
|
|
|
7
7
|
import { ReactSway } from '../index';
|
|
@@ -276,6 +276,25 @@ describe('ReactSway', () => {
|
|
|
276
276
|
});
|
|
277
277
|
|
|
278
278
|
describe('wheel events', () => {
|
|
279
|
+
const expectMinimumScroll = async (onScroll: ReturnType<typeof vi.fn>, minimumDistance: number) => {
|
|
280
|
+
await waitFor(() => {
|
|
281
|
+
const positions = onScroll.mock.calls.map(([position]) => position as number);
|
|
282
|
+
expect(Math.min(...positions)).toBeLessThanOrEqual(-minimumDistance);
|
|
283
|
+
});
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const renderWheelHarness = () => {
|
|
287
|
+
const onScroll = vi.fn();
|
|
288
|
+
const { container } = render(
|
|
289
|
+
<ReactSway autoScroll={false} friction={1} onScroll={onScroll}>
|
|
290
|
+
<div>Content</div>
|
|
291
|
+
</ReactSway>
|
|
292
|
+
);
|
|
293
|
+
const swayContainer = container.querySelector('.react-sway-container') as HTMLElement;
|
|
294
|
+
|
|
295
|
+
return { onScroll, swayContainer };
|
|
296
|
+
};
|
|
297
|
+
|
|
279
298
|
it('applies wheel delta to velocity (fires onPause)', () => {
|
|
280
299
|
const onPause = vi.fn();
|
|
281
300
|
const { container } = render(
|
|
@@ -289,6 +308,52 @@ describe('ReactSway', () => {
|
|
|
289
308
|
expect(onPause).toHaveBeenCalledOnce();
|
|
290
309
|
});
|
|
291
310
|
|
|
311
|
+
it('normalizes line-based wheel deltas before applying velocity', async () => {
|
|
312
|
+
const { onScroll, swayContainer } = renderWheelHarness();
|
|
313
|
+
fireEvent.wheel(swayContainer, { deltaMode: 1, deltaY: 3 });
|
|
314
|
+
|
|
315
|
+
await expectMinimumScroll(onScroll, 14);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('normalizes page-based wheel deltas before applying velocity', async () => {
|
|
319
|
+
const { onScroll, swayContainer } = renderWheelHarness();
|
|
320
|
+
fireEvent.wheel(swayContainer, { deltaMode: 2, deltaY: 1 });
|
|
321
|
+
|
|
322
|
+
await expectMinimumScroll(onScroll, 100);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('normalizes tiny pixel deltas from discrete wheel hardware', async () => {
|
|
326
|
+
const { onScroll, swayContainer } = renderWheelHarness();
|
|
327
|
+
const wheelEvent = new WheelEvent('wheel', {
|
|
328
|
+
bubbles: true,
|
|
329
|
+
cancelable: true,
|
|
330
|
+
deltaMode: 0,
|
|
331
|
+
deltaY: 1,
|
|
332
|
+
});
|
|
333
|
+
Object.defineProperty(wheelEvent, 'wheelDelta', {
|
|
334
|
+
value: -120,
|
|
335
|
+
});
|
|
336
|
+
swayContainer.dispatchEvent(wheelEvent);
|
|
337
|
+
|
|
338
|
+
await expectMinimumScroll(onScroll, 10);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('falls back to legacy wheel deltas when pixel delta is zero', async () => {
|
|
342
|
+
const { onScroll, swayContainer } = renderWheelHarness();
|
|
343
|
+
const wheelEvent = new WheelEvent('wheel', {
|
|
344
|
+
bubbles: true,
|
|
345
|
+
cancelable: true,
|
|
346
|
+
deltaMode: 0,
|
|
347
|
+
deltaY: 0,
|
|
348
|
+
});
|
|
349
|
+
Object.defineProperty(wheelEvent, 'wheelDeltaY', {
|
|
350
|
+
value: -120,
|
|
351
|
+
});
|
|
352
|
+
swayContainer.dispatchEvent(wheelEvent);
|
|
353
|
+
|
|
354
|
+
await expectMinimumScroll(onScroll, 10);
|
|
355
|
+
});
|
|
356
|
+
|
|
292
357
|
it('caps velocity at MAX_VELOCITY', () => {
|
|
293
358
|
const onPause = vi.fn();
|
|
294
359
|
const { container } = render(
|